使用纬度/经度索引mysql表进行地理查找

7

我有一个遗留的InnoDB表格Listing,其中包含带有纬度/经度的商家信息。给定输入的纬度/经度(低于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秒。

我已经尝试在active、approved、deleted、enabled上设置一些索引。是否可以重写查询或添加某些索引以有效地执行此查询 - 而不更改表结构?

+----+-------------+---------+-------------+-------------------------------------------------------------------------------------------------+-----------------------------------------------------------------+---------+------------------------+------+--------------------------------------------------------------------------------------------------------------------------------+
| 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                                                                                                                    |
+----+-------------+---------+-------------+-------------------------------------------------------------------------------------------------+-----------------------------------------------------------------+---------+------------------------+------+--------------------------------------------------------------------------------------------------------------------------------+

非常感谢您的帮助。

2个回答

11

由于你的查询在每一行数据中计算了伟大的圆距离公式和所有超越函数,因此这需要很长时间(如果包括排序则需要两次)。

你能限制搜索的距离范围吗?你可能已经注意到大多数店铺查找网站都有一个下拉菜单项,提供“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子句。但根据我的经验,仅进行纬度BETWEEN搜索即可获得出色的结果。


嗨Ollie,感谢你清晰的解释。然而,到目前为止,即使在纬度上建立索引,我也没有通过缩小搜索范围来显著加快查询的运行速度。我会继续尝试实验。 - javacoder
加快速度的是更改选择方式:选择id、纬度和经度而不是选择listing.*,然后在listing表上执行查询以获取其余数据。 - javacoder
1
我在想是否值得创建一个单独的MyISAM表来存储列表ID和GEOMETRY以进行搜索。有人对这些空间索引的速度有经验吗? - javacoder
我不确定是否可能构建一个可以计算二维距离的索引。索引的目的是以有序的方式预加载搜索的某个方面。 - O. Jones
经度的BETWEEN条件计算方式是否与纬度相同? - MatteoSp

1

你可能想看一下这个问题。不幸的是,你不能在任何不是GEOMETRY类型的列上创建SPATIAL索引,所以至少你需要向表中添加一列。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接