0

我有这样的查询:

SELECT *, (
        6371 * acos (
            cos ( radians(33.577718) )
            * cos( radians( `Latitude` ) )
            * cos( radians( `Longitude` ) - radians(115.846524) )
            + sin ( radians(33.577718) )
            * sin( radians( `Latitude` ) )
        )
    ) AS `distance`
FROM `geopc_cn_places_grouped`
WHERE `Latitude`!=33.577718 AND `Longitude`!=115.846524
HAVING `distance` < 200
ORDER BY `distance` ASC
LIMIT 30;

查询执行总是在 3.5 到 4 秒之间。

我已经通过运行应用了复合索引Latitude,但它并没有减少执行时间。LongitudeALTER TABLE geopc_cn_places_grouped ADD INDEX index_Longitude_Latitude(Longitude, Latitude);

我想知道它为什么运行缓慢以及可以进行哪些优化。

慢查询日志消息显示了这一点

慢查询日志

这是EXPLAIN SELECT查询

解释选择

表结构...

表结构

最后,这是表索引列表

表索引

4

2 回答 2

1

您所写的查询不是sargable。也就是说,它不能利用任何索引。所以,每次你运行它时,你都会对表中的每一行使用那个大的球余弦定律公式。这是一个全表扫描。很可能你的大部分缓慢来自表扫描,因为现代计算机一旦在 RAM 中有数据就可以很快地完成数学运算。

但是,你很幸运。您的搜索会查找候选点 200 英里半径范围内的点。这意味着您可以使用 WHERE ... BETWEEN子句来消除起点以南或以北(纬度)超过 200 英里的点。

为此,您需要知道每个纬度有 69.0 法定英里、60 海里和 111.045 公里。因此,您应该搜索点 ± (200/69) 所以.... 尝试这样的查询。

SELECT *, (
        6371 * acos (
            cos ( radians(33.577718) )
            * cos( radians( `Latitude` ) )
            * cos( radians( `Longitude` ) - radians(115.846524) )
            + sin ( radians(33.577718) )
            * sin( radians( `Latitude` ) )
        )
    ) AS `distance`
FROM `geopc_cn_places_grouped`
WHERE `Latitude`!=33.577718 AND `Longitude`!=115.846524
  AND Latitude BETWEEN 33.577718 - (200/69) AND 33.577718 + (200/69)
HAVING `distance` < 200
ORDER BY `distance` ASC
LIMIT 30;

Latitude然后在您的列上创建一个索引。

CREATE INDEX latsearch ON geopc_cn_places_grouped(Latitude);

Latitude BETWEEN然后我建议的子句将执行索引范围扫描,因此跳过表中的许多行。这是使查询更快的经典 SQL 方式。

这是对这个问题的理想答案的简化。我在这里写了这个问题

于 2021-01-02T17:44:33.817 回答
0

您的查询必须计算每一行的距离。快速的解决方案是使用“边界框”。这将要测试的行数限制为纬度条或经度条。

详细信息(以及更高级的加速):http: //mysql.rjweb.org/doc.php/find_nearest_in_mysql

于 2021-01-02T19:15:08.557 回答