加快对大表的查询

时间:2019-04-08 08:43:39

标签: mysql performance spatial

我正在制作一个具有地理位置的应用程序,所以我有一个城市表,其中有超过200万条记录,并且我想获取最接近记录的已定义纬度和经度,并在两列上都有索引。该代码实现了我想要的功能,但是要花3到4秒钟才能实现。

我试图将索引放在经度和纬度上,并且仅在经度上。我还尝试将纬度和经度分别设置为索引。

SELECT * FROM Cities 
ORDER BY ABS(someLatitude - latitude) ASC, ABS(someLongitude - longitude) ASC 
LIMIT 1

我希望代码在不到一秒钟的时间内执行。我该怎么办?

1 个答案:

答案 0 :(得分:0)

ORDER BY ABS(someLatitude - latitude) ASC,
         ABS(someLongitude - longitude) ASC 

WTF?这样一来,所有纬度非常相似的城市都将汇集在一起​​-甚至包括来自地球另一端的那些城市!

mysql> SELECT country, city, lat, lng FROM `cities`
       WHERE population > 0
       ORDER BY ABS(lat - 36) ASC,
                ABS(lng - 0) ASC LIMIT 10;
+---------+-----------+---------+----------+
| country | city      | lat     | lng      |
+---------+-----------+---------+----------+
| jp      | Okegawa   |      36 |  139.557 |
| us      | Sapulpa   | 35.9986 | -96.1139 |
| us      | Avenal    | 36.0042 | -120.128 |
| cn      | Zhucheng  | 35.9947 |  119.397 |
| us      | Durham    | 35.9939 | -78.8989 |
| us      | Espanola  | 35.9911 |  -106.08 |
| jp      | Chichibu  | 35.9903 |  139.076 |
| us      | Oak Ridge | 36.0103 | -84.2697 |
| ir      | Baneh     | 35.9894 |  45.8953 |
| es      | Tarifa    | 36.0125 | -5.60556 |
+---------+-----------+---------+----------+
10 rows in set (0.90 sec)

请注意,日本,美国,中国,爱尔兰和西班牙的城市基于该ORDER BY彼此“接近”。 (我的列表中有3百万个城市,其中许多人口= 0。)

第一个优化是选择一个最大距离,并在中心周围绘制一个“边界框”。并有一个索引:

    mysql> SELECT country, city, lat, lng FROM `cities` WHERE population > 0
            -> AND lat BETWEEN 36-2 AND 36+2
            -> AND lng BETWEEN -84-3 AND -84+3
            -> LIMIT 10;                                                                                    
        +---------+---------------+---------+----------+
        | country | city          | lat     | lng      |
        +---------+---------------+---------+----------+
        | us      | Gadsden       | 34.0142 | -86.0067 |
        | us      | Cedartown     | 34.0536 |  -85.255 |
        | us      | Acworth       | 34.0658 | -84.6769 |
        | us      | Kennesaw      | 34.0233 | -84.6156 |
        | us      | Woodstock     | 34.1014 | -84.5194 |
        | us      | Mountain Park | 34.0808 | -84.4114 |
        | us      | Roswell       | 34.0231 | -84.3617 |
        | us      | Alpharetta    | 34.0753 | -84.2942 |
        | us      | Duluth        | 34.0028 | -84.1447 |
        | us      | Suwanee       | 34.0514 | -84.0714 |
        +---------+---------------+---------+----------+

使用INDEX(lat)INDEX(lng)INDEX(lat,lng)INDEX(lng,lat)。 (这些索引的优劣程度大致相同。但是仍然必须查看25.7万行,即4度(276英里或444公里宽)条带中的所有行。

如果不限制距离,索引将无用,因为它会查看所有行。

它需要某种ORDER BY子句来计算距离(毕达哥拉斯或Great-Circle)才能获得“最近”。

要真正提高效率,需要付出更多的努力:http://mysql.rjweb.org/doc.php/latlng 那在一定程度上讨论了这个问题为什么很棘手。