MySQL 经纬度范围查询性能优化

MySQL 经纬度范围查询性能优化

知识点:经纬度 1 度等于多少公里?

地球赤道上环绕地球一周走一圈共40075.04公里,而@一圈分成360°,而每1°(度)有60,每一度一秒在赤道上的长度计算如下:
40075.04km/360°=111.31955km
111.31955km/60=1.8553258km=1855.3m

任意两点距离计算公式为

d=111.12cos{1/[sinΦAsinΦB十cosΦAcosΦBcos(λB—λA)]}

其中A点经度,纬度分别为λA和ΦA,B点的经度、纬度分别为λB和ΦB,d为距离。
所以可知每度大概为111千米

指定中心坐标点,查询 5 公里范围内的兴趣点

中心点经度:114.091795,纬度:30.3393,范围:5km

表x_third_data,数据量 3600000 条
maplng为数据表经度字段
maplat为数据表纬度字段
查询结果集数据量 23500 条

优化前 SQL(耗时 9.30 s)

# 设定中心点坐标
set @lng = 114.293488,@lat = 30.589297;

# 查询 5 公里范围内的 POI 数据(优化前)
SELECT
    `id`,`name`,`maplng`,`maplat`
FROM
    `x_third_data`
WHERE
    sqrt(
        (
            (
                ( @lng - `maplng` ) * PI( ) * 12656 * cos(
                    ( ( @lat + `maplat` ) / 2 ) * PI( ) / 180
                ) / 180
                ) * (
                ( @lng - `maplng` ) * PI( ) * 12656 * cos (
                    ( ( @lat + `maplat` ) / 2 ) * PI( ) / 180
                ) / 180
            )
            ) + (
            (
                ( @lat - `maplat` ) * PI( ) * 12656 / 180
                ) * (
                (  @lat - `maplat` ) * PI( ) * 12656 / 180
            )
        )
    ) < 5;

优化后 SQL(耗时 0.65 s)

# 设定中心点坐标
set @lng = 114.293488,@lat = 30.589297;

# 查询 5 公里范围内的 POI 数据(优化后)
SELECT
`id`,`name`,`maplng`,`maplat`
FROM
 `x_third_data`
WHERE
 `maplat` BETWEEN (@lat  - 0.1) AND (@lat + 0.1)
 AND `maplng` BETWEEN (@lng  - 0.1) AND (@lng  + 0.1)
 AND sqrt(
        (
            (
                ( @lng - `maplng` ) * PI( ) * 12656 * cos(
                    ( ( @lat + `maplat` ) / 2 ) * PI( ) / 180
                ) / 180
                ) * (
                ( @lng - `maplng` ) * PI( ) * 12656 * cos (
                    ( ( @lat + `maplat` ) / 2 ) * PI( ) / 180
                ) / 180
            )
            ) + (
            (
                ( @lat - `maplat` ) * PI( ) * 12656 / 180
                ) * (
                (  @lat - `maplat` ) * PI( ) * 12656 / 180
            )
        )
    ) < 5;

查询 5 公里范围内兴趣点并按距离中心点的距离排序

# 设定中心点坐标
set @lng = 114.293488,@lat = 30.589297;

# 查询 5 公里范围内的 POI 数据按距离排序
SELECT
`id`,`name`,`maplng`,`maplat`,
 (st_distance (point (maplng, maplat), point(@lng, @lat)) * 111195) AS distance
FROM
 `x_third_data`
WHERE
 `maplat` BETWEEN (@lat  - 0.1) AND (@lat + 0.1)
 AND `maplng` BETWEEN (@lng  - 0.1) AND (@lng  + 0.1)
 HAVING distance < 1000
 ORDER BY distance ASC

发表评论