[英]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.