数据库
首页 > 数据库> > mysql是否使用我的索引,是否可以改善geokit的性能?

mysql是否使用我的索引,是否可以改善geokit的性能?

作者:互联网

我在rails应用程序中使用了geokit(acts_as_mappable),并且当存在大量模型时,径向或边界搜索的性能会大幅下降(我已经尝试了1-2百万但是这个问题无疑会比这更早出现).

Geokit根据表格中的lat和lng列(纬度和经度)完成所有计算.为了提高性能,geokit通常会添加一个边界框’where’子句,目的是在纬度和经度上使用组合索引来提高性能.然而,对于大量模型来说,它仍然非常慢,而且在我看来,边界框子句应该比它更有帮助.

所以我的问题是,有没有办法让mysql更好地利用组合的lat / lng索引或以其他方式提高geokit sql查询的性能?或者,lat / lng的组合索引可以更有用吗?

编辑:我现在已经开始使用rails了,并且更详细地编写了解决方案here

更多背景

例如,此查询查找给定点10英里内的所有位置. (我添加.length只是为了确定返回多少结果 – 有更好的方法在geokit中说这个,但我想强制一个更典型的SQL查询).

Place.find(:all,:origin=>latlng,:within=>10).length

Mac mini需要大约14秒.这是解释计划

mysql> explain SELECT *, (ACOS(least(1,COS(0.898529183781244)*COS(-0.0157233221653665)*COS(RADIANS(places.lat))*COS(RADIANS(places.lng))+    ->  COS(0.898529183781244)*SIN(-0.0157233221653665)*COS(RADIANS(places.lat))*SIN(RADIANS(places.lng))+    ->  SIN(0.898529183781244)*SIN(RADIANS(places.lat))))*3963.19)
    ->  AS distance FROM `places` WHERE (((places.lat>51.3373601471464 AND places.lat<51.6264998528536 AND places.lng>-1.13302245886176 AND places.lng<-0.668737541138245)) AND ( (ACOS(least(1,COS(0.898529183781244)*COS(-0.0157233221653665)*COS(RADIANS(places.lat))*COS(RADIANS(places.lng))+
    ->  COS(0.898529183781244)*SIN(-0.0157233221653665)*COS(RADIANS(places.lat))*SIN(RADIANS(places.lng))+
    ->  SIN(0.898529183781244)*SIN(RADIANS(places.lat))))*3963.19)
    ->  <= 10)) 
    -> ;
+----+-------------+--------+-------+-----------------------------+-----------------------------+---------+------+-------+----------+-------------+
| id | select_type | table  | type  | possible_keys               | key                         | key_len | ref  | rows  | filtered | Extra       |
+----+-------------+--------+-------+-----------------------------+-----------------------------+---------+------+-------+----------+-------------+
|  1 | SIMPLE      | places | range | index_places_on_lat_and_lng | index_places_on_lat_and_lng | 10      | NULL | 87554 |   100.00 | Using where | 
+----+-------------+--------+-------+-----------------------------+-----------------------------+---------+------+-------+----------+-------------+

所以mysql正在检查87554行,即使结果中的位数是1135(实际上在边界框中的位数只有1323).

这些是索引上的统计信息(使用rails migration add_index:places,[:lat,:lng]):

| Table  | Non_unique | Key_name                         | Seq_in_index | Column_name      | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment |
| places |          1 | index_places_on_lat_and_lng      |            2 | lng              | A         |     1373712 |     NULL | NULL   | YES  | BTREE      |         |

它似乎也与trig计算无关,因为对边界框执行类似的查询会导致查询更简单,但它的执行效果也差不多:

Place.find(:all,:bounds=>GeoKit::Bounds.from_point_and_radius(latlng,10)).length

提供类似的解释计划:

   mysql> explain SELECT * FROM `places` WHERE ((places.lat>51.3373601471464 AND places.lat<51.6264998528536 AND places.lng>-1.13302245886176 AND places.lng<-0.668737541138245)) ;
    +----+-------------+--------+-------+-----------------------------+-----------------------------+---------+------+-------+----------+-------------+
    | id | select_type | table  | type  | possible_keys               | key                         | key_len | ref  | rows  | filtered | Extra       |
    +----+-------------+--------+-------+-----------------------------+-----------------------------+---------+------+-------+----------+-------------+
    |  1 | SIMPLE      | places | range | index_places_on_lat_and_lng | index_places_on_lat_and_lng | 10      | NULL | 87554 |   100.00 | Using where | 
    +----+-------------+--------+-------+-----------------------------+-----------------------------+---------+------+-------+----------+-------------+

解决方法:

普通的B-Tree索引对于像这样的查询来说并不太好.

对于您的查询,范围访问方法用于以下条件:

places.lat > 51.3373601471464 AND places.lat < 51.6264998528536

,这甚至不考虑lon.

如果要使用空间能力,则应将场所保留为点,创建它们的SPATIAL索引并使用MBRContains过滤边界框:

ALTER TABLE places ADD place_point GEOMETRY

CREATE SPATIAL INDEX sx_places_points ON places (place_point)

UPDATE  places
SET     place_point = Point(lat, lon)

SELECT  *
FROM    places
WHERE   MBRContains(LineString(Point(51.3373, -1.1330), Point(51.6264, -0.6687)), place_point)
        AND -- do the fine filtering here

更新:

CREATE TABLE t_spatial (id INT NOT NULL, lat FLOAT NOT NULL, lon FLOAT NOT NULL, coord GEOMETRY) ENGINE=MyISAM;

INSERT
INTO    t_spatial (id, lat, lon)
VALUES  (1, 52.2532, 20.9778);

UPDATE  t_spatial
SET     coord = Point(lat, lon);

这在5.1.35中适用于我.

标签:mysql,sql-execution-plan,performance,geokit
来源: https://codeday.me/bug/20190701/1344440.html