【问题标题】:Indexing a mysql table for geo lookup using latitude/longitude使用纬度/经度索引 mysql 表以进行地理查找
【发布时间】:2011-08-21 06:48:20
【问题描述】:

我有一个遗留的 innodb 表列表,其中包含具有纬度/经度的业务。给定输入的纬度/经度(低于 51.2167/4.41667),查询将按接近度(公里)的顺序返回第一个活动的、启用的、未删除的 30 个企业。与帐户表进行连接以检查列表的有效性。

select 
    listing.*
from
    listing listing ,
    account account 
where
    listing.account_id = account.id 
    and listing.active = 1 
    and listing.approved = 1 
    and listing.deleted = 0 
    and listing.enabled = 1 
    and account.enabled = 1 
    and account.activated_by_user = 1 
group by
    listing.id
having
     111.222569*degrees(acos(sin(radians(listing.latitude))*sin(radians( 51.2167)) +cos(radians(listing.latitude))*cos(radians( 51.2167))*cos(radians(listing.longitude - 4.41667)))) < 250
order by
     111.222569*degrees(acos(sin(radians(listing.latitude))*sin(radians( 51.2167)) +cos(radians(listing.latitude))*cos(radians( 51.2167))*cos(radians(listing.longitude - 4.41667))))
limit 30;

Listing 和 Account 表分别包含超过 50,000 行,但查询仍平均需要 24 秒才能运行。如果没有 order by,则需要 17 秒。

我已经尝试将一些索引设置为活动、批准、删除、启用。我可以重写查询或添加某些索引以有效地执行此查询 - 而不更改表结构吗?

+----+-------------+---------+-------------+-------------------------------------------------------------------------------------------------+-----------------------------------------------------------------+---------+------------------------+------+--------------------------------------------------------------------------------------------------------------------------------+
| id | select_type | table   | type        | possible_keys                                                                                   | key                                                             | key_len | ref                    | rows | Extra                                                                                                                          |
+----+-------------+---------+-------------+-------------------------------------------------------------------------------------------------+-----------------------------------------------------------------+---------+------------------------+------+--------------------------------------------------------------------------------------------------------------------------------+
|  1 | SIMPLE      | listing | index_merge | FKB4DC521D9306A80C,listing_active,listing_approved,listing_enabled,listing_deleted,index_test_1 | listing_active,listing_approved,listing_enabled,listing_deleted | 1,1,1,1 | NULL                   | 3392 | Using intersect(listing_active,listing_approved,listing_enabled,listing_deleted); Using where; Using temporary; Using filesort |
|  1 | SIMPLE      | account | eq_ref      | PRIMARY,account_enabled,account_activated_by_user,index_test_2                                  | PRIMARY                                                         | 8       | ctm.listing.account_id |    1 | Using where                                                                                                                    |
+----+-------------+---------+-------------+-------------------------------------------------------------------------------------------------+-----------------------------------------------------------------+---------+------------------------+------+--------------------------------------------------------------------------------------------------------------------------------+

非常感谢任何帮助。

【问题讨论】:

    标签: mysql database geospatial latitude-longitude indexing


    【解决方案1】:

    这需要很长时间,因为您的查询正在为 50k 行表中的每一行计算具有所有超越函数的大圆距离公式一次(包括排序时两次)。

    您可以限制搜索的距离范围吗?您可能已经注意到,大多数商店查找网络应用程序都有一个下拉菜单项,可以选择“5 英里以内”、“10 英里以内”等等。

    如果你能做到这一点,你应该在你的搜索中添加一个 WHERE 子句,并通过在你的 LATITUDE 列上放置一个索引来优化它。假设您使用值 RANGELIMIT 作为搜索范围限制,以英里为单位。

    试试这个子句

    WHERE LISTING.LATITUDE BETWEEN LOCATION.LATITUDE - (RANGELIMIT * 1.1508/60) 
                               AND LOCATION.LATITUDE + (RANGELIMIT * 1.1508/60)
    

    这是可行的,因为海里几乎完全等于一分钟 (1/60) 度的纬度。 1.1508 系数将海里转换为法定英里。

    我建议的子句将使用纬度索引来缩小搜索范围,并且您计算大圆距离的频率会大大降低。

    您还可以在经度上包含 BETWEEN 子句。但根据我的经验,只需进行 latitude BETWEEN 搜索即可获得出色的结果。

    【讨论】:

    • 嗨奥利,感谢您的明确解释。但是,到目前为止,即使使用纬度索引,我也没有太多运气通过根据您的建议缩小搜索范围来显着加快查询速度。我会再做一些实验。
    • 加快速度的是更改选择:SELECT id、纬度、经度而不是 SELECT 列表。* 并随后对列表执行查询以获取其余数据。
    • 我想知道是否值得创建一个单独的 MyISAM 表来存储列表 id 和 GEOMETRY 以进行搜索。有人对这些空间索引的速度有经验吗?
    • 我不确定是否可以建立一个可以计算二维距离的索引。索引的目的是以有序的方式预加载搜索的某些方面。
    • 经度 BETWEEN 条件的计算方式是否与纬度相同?
    【解决方案2】:

    您可能想看看this question。不幸的是,您不能在任何不是GEOMETRY 类型的列上创建SPATIAL 索引,因此至少您需要向表中添加一列。

    【讨论】:

      猜你喜欢
      • 2018-07-26
      • 2013-05-04
      • 2019-08-31
      • 2016-05-08
      • 2021-01-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-09-03
      相关资源
      最近更新 更多