我在 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 |
+----+-------------+--------+-------+-----------------------------+-----------------------------+---------+------+-------+----------+-------------+
最佳答案
普通 B-Tree
索引对于这样的查询来说不太好。
对于您的查询,range
访问方法用于以下条件:
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 - mysql是否使用了我的索引,geokit的性能可以提高吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/1333984/