【发布时间】:2009-08-26 11:13:44
【问题描述】:
我在 rails 应用程序中使用 geokit (acts_as_mappable),当有大量模型时,径向或边界搜索的性能会大大降低(我尝试过 1-200 万个,但问题无疑会出现比这更早)。
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 迁移 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 |
+----+-------------+--------+-------+-----------------------------+-----------------------------+---------+------+-------+----------+-------------+
【问题讨论】:
标签: mysql performance sql-execution-plan geokit