Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
565 views
in Technique[技术] by (71.8m points)

mysql - How to optimize complex calculation query execution time?

I have a query like this :

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;

The query execution is always somewhere between 3.5 and 4 seconds.

I have applied a composite index to Latitude and Longitude by running ALTER TABLE geopc_cn_places_grouped ADD INDEX index_Longitude_Latitude(Longitude, Latitude);, but it doesn't reduce the execution time.

I want to know why it's running slow and what possible optimizations can be done.

The slow query log message shows this

Slow Query Log

and this is the EXPLAIN SELECT query

EXPLAIN SELECT

Table Structure...

Table Structure

and lastly, here is the table index list

Table Indexes


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

Your query as written isn't 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. It's likely that most of your slowness comes from the table scan, because modern computers do the math pretty quickly once they have the data in RAM.

But, you're in luck. Your search looks for points within a 200 statute mile radius of your candidate point. That means you can usea WHERE ... BETWEEN clause to eliminate points that are more than 200 miles south or north (latitude) of your starting point.

To do this you need to know there are 69.0 statute miles, 60 nautical miles, and 111.045 km in each degree of latitude. Therefore you should search for point ± (200/69) So.... try a query like this.

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;

Then create an index on your Latitude column.

CREATE INDEX latsearch ON geopc_cn_places_grouped(Latitude);

The Latitude BETWEEN clause I suggest will then do an index range scan and so skip many of the rows in your table. That's the classic SQL way of making queries faster.

This is a simplification of the ideal answer to this question. I wrote up this problem here.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...