mysql - mysql是否使用了我的索引,geokit的性能可以提高吗?

标签 mysql performance sql-execution-plan geokit

我在 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/

相关文章:

sql-server - 用于将执行 xml 计划可视化为 HTML 的工具

Mysql查询第一个元素信息以及最后一个元素计数

php - 如何在 PHP 中将数组分组并编码为 JSON?

c - 使用深度嵌套的指针

java - 在什么情况下 do-while 比 while 更有效率?

sql - DBMS_XPLAN.DISPLAY_CURSOR 与未使用gather_plan_statistics 提示的解释计划

python - 使用 SQLAlchemy ORM 批量插入多个表

java - JOOQ 如何做 UNION 和 WHERE IN

python - 替代合并排序的效率?

sql - Mysql 查询性能 -