MySQL空间索引不走B+树路径,而是基于MBR逐层剪枝的递归相交判断;仅ST_Contains等空间谓词能触发索引,且需MyISAM引擎、NOT NULL约束和正确SRID支持。

MySQL 对空间数据的查询不走普通 B+Tree 索引路径,R-Tree 索引的检索是基于最小边界矩形(MBR)逐层剪枝的递归过程,不是“匹配值”,而是“判断相交”。
R-Tree 查询本质是 MBR 相交判断
MySQL 的 R-Tree 索引不支持等值查找(比如 WHERE geom = POINT(1,1)),只响应空间谓词函数产生的范围语义。真正触发索引扫描的是类似 ST_Contains()、ST_Within()、ST_Intersects() 这类函数,且它们内部会自动提取查询几何对象的 MBR,并与索引节点的 MBR 做重叠判断。
- 每个 R-Tree 节点(包括内部节点和叶子节点)都维护一个
MBR,即覆盖其所有子项的最小矩形 - 查询时,从根节点开始,仅递归进入那些
MBR与查询MBR相交的子节点 - 叶子节点中存储实际几何对象,但最终是否满足
ST_Contains(a,b)还需做精确计算——索引只负责快速过滤掉明显不相关的页,不保证结果完全精确 - 这意味着:即使
ST_Intersects()返回 true,也必须由 MySQL Server 层对候选行再做一次精确几何运算
只有 MyISAM 支持原生 R-Tree 空间索引
InnoDB 在 5.7 之后虽支持 POINT 等空间类型,但**不支持 R-Tree 索引**;它把空间列当普通 BLOB 存储,ST_* 函数只能全表扫描或依赖隐式转换后的前缀索引(效果极差)。真要走 R-Tree,必须用 MyISAM 表引擎。
- 建表时显式指定
ENGINE=MyISAM,否则CREATE SPATIAL INDEX会静默失败或退化为普通索引 -
SPATIAL INDEX只能建在单个空间列上,不能用于联合索引 - 空间列必须为
NOT NULL,否则建索引报错ERROR 1167: The used storage engine can't index column 'geom' - MyISAM 的 R-Tree 是静态构建的,不支持并发写入下的在线分裂/合并,高并发写入易导致索引碎片和性能抖动
常见误用:ST_Distance 和 ORDER BY 导致索引失效
很多人想查“离某点最近的 10 个 POI”,直接写 ORDER BY ST_Distance(geom, POINT(1,1)) LIMIT 10,这不会用上 R-Tree 索引——因为 ST_Distance() 是逐行计算的标量函数,优化器无法将其下推到索引扫描层。
- 正确做法是先用
ST_DWithin(geom, POINT(1,1), 1000)(单位是度或米,取决于 SRID)圈出粗略候选集,再在结果里排序 -
ST_DWithin内部会生成一个缓冲区 MBR,能触发 R-Tree 的范围剪枝 - 若没设
SRID或设错(如用 WGS84 坐标却按平面距离算),ST_DWithin可能返回空或错误结果,且不报错 - MyISAM 表没有事务,
INSERT/UPDATE后需REPAIR TABLE才能重建空间索引,否则后续查询可能漏数据
实际执行计划里看不到 “Using spatial index”
MySQL 的 EXPLAIN 对空间索引支持极弱:即使走 R-Tree,type 字段通常显示 ALL 或 range,key 字段为空或显示错误索引名,Extra 里也不会出现 “Using spatial index” 这类提示。
- 验证是否命中索引,唯一可靠方式是对比加索引前后
SELECT COUNT(*)或ST_Intersects()查询的执行时间(尤其数据量 >10 万行时) - 开启
profiling并看Handler_read_*状态变量:Handler_read_next显著下降说明索引生效(MyISAM 引擎特有) - 注意:MySQL 8.0+ 的 InnoDB 已支持
ST_*函数的索引优化(通过隐藏的rtree结构),但仅限于ST_Covers/ST_Coveredby等少数函数,且需显式指定SPATIAL关键字并配合GEOMETRY列类型
R-Tree 不是“更快的 B+Tree”,它是另一套逻辑:靠 MBR 重叠做粗筛,靠函数语义定边界,靠引擎限制保行为。用错引擎、写错函数、忽略 SRID,三者任一都会让整个空间索引形同虚设。


















