如何优化复杂计算查询的执行时间?
How to optimize complex calculation query execution time?
我有这样的查询:
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);
对 Latitude
和 Longitude
应用了复合索引,但它并没有减少执行时间。
我想知道为什么它 运行 慢以及可以进行哪些优化。
慢速查询日志消息显示了这一点
这是 EXPLAIN SELECT
查询
Table结构...
最后,这里是 table 索引列表
您写的查询不是 sargable. That is, it cannot exploit any index. So, each time you run it, you use that big spherical cosine law formula for every row in your table. It's a full table scan。您的大部分速度缓慢很可能来自 table 扫描,因为现代计算机一旦将数据存储在 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,因此会跳过 table 中的许多行。这是加快查询速度的经典 SQL 方法。
这是对这个问题的理想答案的简化。 I wrote up this problem here.
您的查询必须计算每一行的距离。快速的解决方案是使用“边界框”。这将要测试的行数限制为纬度条纹或经度条纹。
详细信息(以及更高级的加速):http://mysql.rjweb.org/doc.php/find_nearest_in_mysql
我有这样的查询:
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);
对 Latitude
和 Longitude
应用了复合索引,但它并没有减少执行时间。
我想知道为什么它 运行 慢以及可以进行哪些优化。
慢速查询日志消息显示了这一点
这是 EXPLAIN SELECT
查询
Table结构...
最后,这里是 table 索引列表
您写的查询不是 sargable. That is, it cannot exploit any index. So, each time you run it, you use that big spherical cosine law formula for every row in your table. It's a full table scan。您的大部分速度缓慢很可能来自 table 扫描,因为现代计算机一旦将数据存储在 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,因此会跳过 table 中的许多行。这是加快查询速度的经典 SQL 方法。
这是对这个问题的理想答案的简化。 I wrote up this problem here.
您的查询必须计算每一行的距离。快速的解决方案是使用“边界框”。这将要测试的行数限制为纬度条纹或经度条纹。
详细信息(以及更高级的加速):http://mysql.rjweb.org/doc.php/find_nearest_in_mysql