我在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 |
+----+-------------+--------+-------+-----------------------------+-----------------------------+---------+------+-------+----------+-------------+
因此,即使结果中的位置数为 1135(实际上边界框中的位置数仅为 1323),mysql 仍在检查 87554 行。
这些是索引上的统计信息(使用 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 | |
它似乎也与三角计算无关,因为对边界框执行类似的查询会导致查询更简单,但它的性能同样很差:
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 |
+----+-------------+--------+-------+-----------------------------+-----------------------------+---------+------+-------+----------+-------------+