含JOIN的地理查询优化:PlanetScale慢查询问题求解
查询性能瓶颈分析与优化方案
瓶颈原因
- 冗余连接与重复数据处理开销:原查询通过
JOIN flaut.City main关联主城市,但main.code = 'MOW'仅返回单条记录,却让所有符合条件的neighbour行与之做笛卡尔积连接,无意义放大中间结果;同时JOIN flaut.Airport因单城市多机场产生重复行,后续DISTINCT需对结果集排序去重,额外消耗CPU和内存。 - CPU密集型计算无差别执行:距离计算依赖大量三角函数,这类计算对CPU资源消耗极高。原查询先对所有满足
WHERE条件的行计算距离,再通过HAVING过滤,导致大量不必要的计算——这就是引擎负载高的核心原因:IO读取量不大,但CPU被三角函数计算占满。 - 过滤时机滞后:
HAVING在结果集生成后才执行过滤,而非在WHERE阶段提前缩小范围,进一步增加无效计算量。
优化方案
1. 重构查询逻辑,消除冗余操作
用EXISTS替代JOIN Airport,仅验证城市存在关联机场即可,避免因多机场产生重复行,直接去掉DISTINCT;同时用子查询单独获取主城市经纬度,消除无意义的表连接:
SELECT neighbour.*, (6371 * acos(cos(radians(main_lat)) * cos(radians(neighbour.latitude)) * cos(radians(neighbour.longitude) - radians(main_lon)) + sin(radians(main_lat)) * sin(radians(neighbour.latitude)))) AS distance FROM flaut.City neighbour, (SELECT latitude AS main_lat, longitude AS main_lon FROM flaut.City WHERE code = 'MOW') AS main WHERE neighbour.code != 'MOW' AND neighbour.population IS NOT NULL AND EXISTS (SELECT 1 FROM flaut.Airport WHERE cityCode = neighbour.code) -- 经纬度边界框预过滤,大幅减少精确距离计算行数 AND neighbour.latitude BETWEEN main_lat - (800 / 111.045) AND main_lat + (800 / 111.045) AND neighbour.longitude BETWEEN main_lon - (800 / (111.045 * COS(RADIANS(main_lat)))) AND main_lon + (800 / (111.045 * COS(RADIANS(main_lat)))) HAVING distance < 800 ORDER BY distance;
2. 提前缩小计算范围
通过经纬度边界框预过滤(111.045为每度纬度对应的公里数,经度公里数随纬度变化需乘以余弦值),先筛选出大致在800公里范围内的城市,再计算精确距离,大幅降低CPU消耗。
3. 添加针对性索引
- 给
flaut.City创建联合索引:CREATE INDEX idx_city_code_lat_lon_pop ON flaut.City(code, latitude, longitude, population);,快速定位主城市经纬度,同时加速neighbour的过滤与字段读取。 - 给
flaut.Airport创建索引:CREATE INDEX idx_airport_citycode ON flaut.Airport(cityCode);,加速EXISTS子查询的存在性判断。
内容的提问来源于stack exchange,提问作者JamesJGoodwin
相关产品推荐
相关产品推荐

