MySQL加间隙锁会阻塞无关联插入操作,根本原因是RR隔离级别下InnoDB将索引间隙视为需保护的逻辑单元,只要新插入值落入已锁间隙(如(10,30)),无论记录是否存在或是否业务相关,均被阻塞。

MySQL加间隙锁会阻塞无关联的插入操作,根本原因不是“插入本身有问题”,而是InnoDB在REPEATABLE READ隔离级别下,把索引间隙当作一个需要保护的逻辑单元——只要新插入的主键或索引值落在已被锁定的间隙内,哪怕那条记录根本不存在、且和之前事务毫无业务关系,也会被立刻阻塞。
间隙锁锁的是“空隙”,不是“数据”
比如表里主键有 10 和 30,那么 (10, 30) 就是一个间隙。事务A执行了 SELECT * FROM t WHERE id > 10 AND id ,InnoDB就会加Next-Key Lock,实际锁住的是这个开区间。此时事务B执行 INSERT INTO t (id) VALUES (15),虽然15这条记录当前不存在、也不属于任何已有业务上下文,但15 ∈ (10, 30),插入意向锁申请失败,直接卡住。
- 间隙锁不依赖具体行存在与否,只看索引顺序位置
- LOCK_DATA显示的是边界值(如
10, 30),不是你INSERT的值本身 - 空表最危险:
SELECT * FROM t WHERE id = 5 FOR UPDATE会锁(-∞, +∞)整个主键空间
看似无关的插入,其实共享同一索引路径
所谓“无关联”,常指两个事务操作不同字段、不同业务模块,但只要它们最终都落到同一个索引结构上(尤其是主键或二级索引),就可能撞上同一段间隙。例如:
- 订单服务按
order_no(唯一索引)插入,用的是自增ID生成器 → 插入值密集连续 - 用户服务按
user_id(也是主键)插入,但ID来自外部系统 → 值随机分散 - 两者并发时,若都落在已由某次范围查询锁定的
(100, 200)区间内,就会互相阻塞
这种“无关联”只是应用层视角;对InnoDB来说,所有插入都走聚簇索引,间隙是共享资源。
面向 CRM 系统的 MySQL 8 数据库模式设计。当用户需要为客户关系管理(CRM)系统设计、评审、优化或生成数据库模式时,应使用此项技能。
为什么不能等锁释放后再插?
Insert Intention Lock不是占位符,而是安全探测信号:它必须在写入前确认“这个空隙当前是否允许插入”。如果允许等待,就可能错过其他事务刚刚插入的新记录,破坏RR级别的可重复读语义。
- InnoDB选择立刻拒绝,而非挂起等待,是为了避免锁等待链失控
- SHOW ENGINE INNODB STATUS里看到的
lock_mode X locks gap before rec insert intention waiting就是这个探测失败的明确标志 - 多个事务同时申请同一间隙的插入意向锁,其中一个会被选为死锁牺牲者,而不是排队
查锁源比猜逻辑更可靠
间隙锁本身不显式出现在 information_schema.INNODB_TRX 中,你看到的永远是“INSERT卡住”,根源却可能藏在几分钟前一条 SELECT ... FOR UPDATE 里。
- 先定位等待事务:
SELECT * FROM information_schema.INNODB_TRX WHERE TRX_STATE = 'LOCK WAIT' - 再查锁详情:
SELECT * FROM performance_schema.data_locks WHERE LOCK_TRX_ID = 'xxx' - 重点关注
LOCK_MODE含GAP或NEXT-KEY,且LOCK_DATA显示类似5, 10的区间 -
LOCK_DATA为空?大概率是全表扫描触发的隐式间隙锁,说明缺索引
真正麻烦的不是间隙锁存在,而是它边界模糊、不可见、又影响广泛——你得主动去查,而不是等它报错。










