如何优化复杂计算查询的执行时间?

Posted

技术标签:

【中文标题】如何优化复杂计算查询的执行时间?【英文标题】:How to optimize complex calculation query execution time? 【发布时间】:2021-01-01 06:22:01 【问题描述】:

我有一个这样的查询:

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 秒之间。

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

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

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

这是EXPLAIN SELECT 查询

表结构...

最后,这里是表格索引列表

【问题讨论】:

研究在 mysql 上创建地理空间索引:dev.mysql.com/doc/refman/8.0/en/creating-spatial-indexes.html 好的,现在检查... 不要使用公式 - 使用空间数据类型和 ST_Distance() 函数。 知道了,谢谢,我正在研究中 警告:latitude != ... 可能由于舍入错误而无法正常工作。考虑使用id 的地方来避免。 【参考方案1】:

您所写的查询不是sargable。也就是说,它不能利用任何索引。所以,每次你运行它时,你都会对表中的每一行使用那个大的球余弦定律公式。这是full table scan。很可能您的大部分缓慢都来自表扫描,因为现代计算机一旦将数据放入 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 子句然后会执行index range scan 并因此跳过表中的许多行。这是加快查询速度的经典 SQL 方式。

这是对这个问题的理想答案的简化。 I wrote up this problem here.

【讨论】:

你是个天才!现在执行时间减半了,我对结果完全满意,太棒了,谢谢!【参考方案2】:

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

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

【讨论】:

以上是关于如何优化复杂计算查询的执行时间?的主要内容,如果未能解决你的问题,请参考以下文章

Prometheus-定期执行的规则

如何优化执行嵌套在 group-by 子句中的计数的 SQL 查询?

强制SQL Server执行计划使用并行提升在复杂查询语句下的性能

sqlite的缺点和限制

优化复杂查询

如何优化在c#react中执行查询的时间