r-tree索引不支持行级锁,加索引或dml操作均触发表锁;因mbr无全局线性序,故不支持间隙锁与临键锁。

R-tree 索引不支持行级锁,加索引时必然触发表锁
MySQL 的空间索引(R-tree)仅在 MyISAM 和 InnoDB(5.7+ 支持 POINT 等类型)中可用,但它的底层实现基于 R-tree 结构,而 MySQL 的行锁机制(如记录锁、间隙锁)完全依赖 B+Tree 索引的有序性和键值定位能力。R-tree 无法支撑 InnoDB 的行级锁加锁逻辑,因此任何对空间列的 DML 操作(INSERT/UPDATE/DELETE)或建索引操作,都会退化为表级锁。
-
ALTER TABLE t ADD SPATIAL INDEX idx_geom (geom)在InnoDB中会持有X表锁,阻塞所有并发读写,等同于LOCK TABLES t WRITE - 即使查询语句使用
MBRContains()或ST_Within()走了空间索引,InnoDB 仍无法按“某个多边形覆盖的几行”精准加行锁——它只能对整张表加意向排他锁(IX),再配合全表扫描式过滤,实际执行中常升级为表锁 -
MyISAM下更直接:所有索引(含空间索引)都只支持表锁,SELECT ... FROM t WHERE MBRIntersects(geom, ST_GeomFromText(...))会自动加READ锁,阻塞写;写操作则加WRITE锁,阻塞一切
空间查询不会触发间隙锁或临键锁
间隙锁(GAP LOCK)、临键锁(Next-Key Lock)是 InnoDB 为防止幻读、在 B+Tree 索引的「键区间」上施加的锁,依赖严格有序的键比较。而 R-tree 的键是多维边界矩形(MBR),没有全局线性序,MySQL 无法定义“前一个 MBR”或“后一个 MBR”,因此:
- 即使事务隔离级别是
REPEATABLE READ,空间条件查询(如WHERE ST_Distance(geom, POINT) )也不会产生间隙锁 - 无法用空间索引做唯一性约束(
UNIQUE SPATIAL INDEX不被允许),所以也不存在因唯一冲突回退导致的锁等待 - 这意味着空间查询的并发度看似高,但代价是:可能读到未提交的插入(若其他事务正写入新几何对象且尚未提交),即存在不可重复读风险
建空间索引时的锁行为与存储引擎强相关
同一 ADD SPATIAL INDEX 语句,在不同引擎下锁表现完全不同,不能假设“加索引就锁表”是统一行为:
-
MyISAM:建索引全程独占表,期间禁止所有读写,错误信息类似ERROR 1021 (HY000): Disk full(实为锁等待超时伪装) -
InnoDB(5.7+):建空间索引会触发在线 DDL 的ALGORITHM=INPLACE吗?不会。空间索引不支持INPLACE,必须走COPY算法,即重建表 → 持有X表锁 → 复制数据 → 原子替换,整个过程阻塞所有 DML -
InnoDB(8.0.24+)新增对POINT类型的空间函数优化,但仍未改变加锁粒度:只要涉及GEOMETRY列的索引维护,就绕不开表级排他控制
真正容易被忽略的点:空间索引失效时的锁升级更隐蔽
当空间查询因隐式类型转换、函数包裹(如 ST_Buffer(geom, 1))、或坐标系不匹配(SRID 不一致)导致无法命中索引时,InnoDB 会放弃 R-tree 走全表扫描——此时本该是表锁的场景,反而可能触发更糟的结果:
- 全表扫描过程中,每读一行就尝试加
SELECT ... FOR UPDATE式的行锁(如果语句带锁),但由于没走索引,InnoDB 实际对每一行都加记录锁(Record Lock),最终效果 ≈ 持有全部行锁 + 意向锁,极易引发死锁 - 而用户只看到
Lock wait timeout exceeded,却查不到显式的SPATIAL关键字报错,误以为是普通索引问题 - 验证方式很简单:
EXPLAIN FORMAT=JSON查看key字段是否为NULL或非空间索引名;再用SHOW ENGINE INNODB STATUS\G观察TRANSACTIONS部分的锁等待链











