繁体   English   中英

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

[英]Is mysql using my index or not, and can the performance of geokit be improved?

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

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

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

编辑:我现在已经使用rails了,并在这里更详细地编写了解决方案

更多背景

例如,此查询查找给定点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索引对于像这样的查询来说并不太好。

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

places.lat > 51.3373601471464 AND places.lat < 51.6264998528536

,这甚至不考虑lon

如果要使用空间能力,则应将场所保留为Points ,创建它们的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适用于我。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM