MySQL 8中使用Point类型与ST_Distance_Sphere查找最近地点
结合你的场景和给出的查询结果,逐个解答你的问题:
1. 这个方案是否最优?写法是否正确?
你的写法是正确的:ST_Distance_Sphere是MySQL 5.7及以上版本提供的专门针对WGS84(SRID 4326)坐标的球面距离计算函数,比手动实现Haversine公式更简洁,而且MySQL对它做了底层优化,计算效率更高。
但要说“最优”的话,得看你的数据规模——从EXPLAIN结果看你的表有11万+行,当前写法会全表计算每个点到目标坐标的距离,再做全表排序(Using filesort),数据量再增大的话性能会明显下降。如果你的需求只是获取前N个最近的地点(比如最近10个),可以加上LIMIT N来避免全表排序,能提升不少性能;如果要进一步优化,建议结合空间范围过滤先缩小计算范围(后面会说到空间索引的用法)。
2. 该方法会使用coordinates列上的空间索引吗?
从你给出的EXPLAIN结果(type: ALL、key: NULL)和Handler_*状态(Handler_read_rnd_next数值接近表行数)可以明确:当前写法没有用到空间索引。
原因是ST_Distance_Sphere这类距离计算函数在SELECT子句或ORDER BY中使用时,MySQL无法触发空间索引。空间索引的典型适用场景是范围查询,比如用ST_DWithin先圈出目标点周围一定距离内的点,再对这些点计算精确距离排序,这样就能利用空间索引过滤掉大部分无关数据。
举个优化后的例子(假设你要找10公里内的最近地点):
SELECT *, ST_Distance_Sphere(`place`.`coordinates`, ST_GeomFromText('Point($longitude $latitude)', 4326)) AS distance FROM place WHERE ST_DWithin(`place`.`coordinates`, ST_GeomFromText('Point($longitude $latitude)', 4326), 10000) -- 单位:米,这里代表10公里范围 ORDER BY distance ASC;
前提是你已经给coordinates列创建了空间索引:
CREATE SPATIAL INDEX idx_place_coordinates ON place(coordinates);
这样ST_DWithin会触发空间索引,先过滤出10公里内的点,再计算距离排序,性能会比全表扫描好很多。
3. 使用ST_Distance_Sphere时是否需要指定地球半径?
你自己的补充是对的:不需要。ST_Distance_Sphere默认采用WGS84坐标系标准的地球半径(6370986米),计算出来的距离单位是米,结果是准确的,不需要额外传入地球半径参数。
内容的提问来源于stack exchange,提问作者Matt Kieran

