普通B-TREE索引对经纬度字段完全无效:因B-TREE按字典序组织数据,无法高效处理二维空间查询(如“附近5公里”),lat/lon范围查询仍需全表或大范围扫描,球面距离等非线性条件更无法优化。

普通B-TREE索引对经纬度字段完全无效
直接结论:给 latitude 和 longitude 两个 DECIMAL 字段建 INDEX(lat, lon),对“附近5公里内所有点”这类查询几乎没用——执行计划里照样 type: ALL 或 type: range 扫大量行,不是真索引命中。
根本原因在于:B-TREE 只能按单字段或字段组合的字典序组织数据,而经纬度是二维空间坐标,lat BETWEEN ? AND ? AND lon BETWEEN ? AND ? 这种“矩形框”查询,在 B-TREE 上无法跳过无关区域;更别说球面距离、圆形范围这些非线性条件了。
- 错误现象:
WHERE ST_Distance_Sphere(coordinates, ST_GeomFromText('POINT(121.4737 31.2304)', 4326)) 不加空间索引时,哪怕只有 10 万条记录,也常超 2 秒 - 真实场景:地图 App 拉取周边 POI、物流系统查“3km 内可派送门店”,必须靠空间索引把查询从 O(n) 降到接近 O(log n)
- 兼容性注意:MyISAM 早年支持 SPATIAL,但 InnoDB 自 MySQL 5.7 起才完整支持——确认你的表引擎是
InnoDB,否则CREATE SPATIAL INDEX会静默失败或不生效
POINT + SRID 4326 是唯一可落地的起点
别再用两个浮点字段存经纬度。MySQL 的空间能力只在 POINT 类型上激活,且必须绑定正确坐标系。
SRID 4326 不是可选项,是强制前提:漏掉它,ST_Distance_Sphere() 返回 NULL,ST_Contains() 总是 0,而 MySQL 几乎不报错,排查起来像幽灵 bug。
- 建表必须写:
coordinates POINT NOT NULL SRID 4326 - 插入必须带 SRID:
ST_GeomFromText('POINT(121.4737 31.2304)', 4326)—— 第二个参数不能省,也不能写成0或4326.0 - 顺序别颠倒:
POINT(longitude latitude),上海是POINT(121.4737 31.2304),不是POINT(31.2304 121.4737);输反了,所有距离和包含判断全错
SPATIAL INDEX 只对特定查询模式生效
建了空间索引不等于自动加速所有地理查询。它只优化三类操作:矩形范围(MBRContains)、邻近排序(ST_Distance_Sphere + HAVING)、几何关系(ST_Contains, ST_Intersects)。
典型误用:SELECT * FROM locations WHERE ST_Distance_Sphere(coordinates, ?) —— 这种写法无法利用索引,必须改用 <code>ST_DWithin(MySQL 8.0.29+)或先用 MBRContains 做粗筛。
- 推荐写法(MySQL 5.7–8.0.28):
SELECT *, ST_Distance_Sphere(coordinates, ST_GeomFromText('POINT(121.4737 31.2304)', 4326)) AS dist FROM locations WHERE MBRContains( ST_GeomFromText('POLYGON((121.46 31.22, 121.46 31.24, 121.49 31.24, 121.49 31.22, 121.46 31.22))', 4326), coordinates ) HAVING dist <= 5000; -
MBRContains()走空间索引,快速过滤出可能候选;HAVING再做精确球面距离计算 - MySQL 8.0.29+ 可用
ST_DWithin(coordinates, ?, 5000),它内部自动组合 MBR 粗筛 + 精确计算,且能走索引
空间索引失效的隐蔽坑点
最常被忽略的是函数包裹列:只要 WHERE 条件里对空间字段用了任何函数(哪怕只是 ST_X(coordinates)),索引立刻失效。
另一个硬约束:空间列必须为 NOT NULL,且不能有前缀长度(比如 coordinates(255)),否则 CREATE SPATIAL INDEX 会报错 ERROR 1170 (42000): BLOB/TEXT column 'coordinates' used in key specification without a key length —— 这其实是提示你字段类型不对,不是让你加前缀。
- 检查是否生效:
EXPLAIN SELECT ...看key列是否显示你的空间索引名,rows是否明显下降 - 不要在空间列上建其他类型索引(如
INDEX(coordinates)),MySQL 不允许,会报错ERROR 1167 (HY000): The used table type doesn't support SPATIAL indexes(即使引擎是对的,也可能因字段定义问题触发) - 批量导入后务必
ANALYZE TABLE locations,让优化器更新空间统计信息,否则可能选错执行计划
空间索引不是“开了就快”的黑盒,它依赖正确的类型、严格的 SRID、匹配的查询模式,以及避开函数包裹和 NULL 值陷阱——任何一个环节松动,性能就会断崖式回落。


















