RC级别下仍会出现gap before rec锁,是因为INSERT/REPLACE等操作触发唯一索引或主键的重复性校验,InnoDB必须加间隙锁防止并发插入相同值,即使在RC隔离级别下也无法避免。

RC级别下为什么还会出现 gap before rec 锁
MySQL 在 READ COMMITTED 隔离级别下**默认不使用间隙锁做范围扫描**,但间隙锁并未完全消失——它仍会出现在唯一性约束检查场景中。这是官方文档明确说明却常被忽略的细节。
常见错误现象:INSERT INTO t (id, name) VALUES (100, 'x') 被阻塞,SHOW ENGINE INNODB STATUS 显示事务在等待 gap before rec,而当前隔离级别明明是 RC。
- 根本原因不是“隔离级别失效”,而是该
INSERT触发了唯一索引(或主键)的重复校验,InnoDB 必须加间隙锁防止其他事务同时插入相同值 - 外键约束检查也会触发间隙锁,哪怕在 RC 下
-
UPDATE ... WHERE unique_col = ?不会加间隙锁,但INSERT或REPLACE一定会走唯一性检查路径
如何确认是不是唯一性检查导致的间隙锁
关键看锁日志里是否同时出现 gap before rec 和 insert intention —— 这是插入意向锁与间隙锁冲突的典型信号,基本可锁定为唯一性校验场景。
实操建议:
- 执行
SELECT * FROM information_schema.INNODB_TRX查出阻塞事务 ID,再查information_schema.INNODB_LOCK_WAITS确认锁等待链 - 重点检查被等待的锁对象是否落在唯一索引上(如
PRIMARY或uk_name),且LOCK_MODE是X,GAP - 用
EXPLAIN FORMAT=tree看插入语句是否命中唯一索引;如果possible_keys为空或用了全表扫描,那间隙锁范围可能远超预期
唯一索引缺失或失效时的连锁反应
当你要插入的字段本应有唯一索引,但实际没建,或者索引被 DROP 后未重建,InnoDB 无法做高效唯一性检查,就会退化为扫描整个索引范围并加更宽的间隙锁——这在 RC 下也会发生,且极难排查。
典型表现:
- 插入语句响应明显变慢,尤其在大表上
-
SHOW ENGINE INNODB STATUS中显示锁住的间隙跨度极大(如(1000, 5000000)) - 同一时间多个插入请求互相阻塞,即使插入值完全不同
解决办法只有两个:补上缺失的唯一索引,或改用应用层幂等控制(如先 SELECT 再 INSERT ... ON DUPLICATE KEY UPDATE)。
RC 下避免间隙锁干扰的底线操作
如果你的业务能接受 RC 隔离,并希望彻底规避间隙锁带来的插入阻塞,必须满足三个硬条件:
- 所有写入字段都有对应唯一索引(主键或
UNIQUE约束),不能依赖业务逻辑保证唯一 - 不使用外键(
FOREIGN KEY),否则外键检查强制启用间隙锁 - 避免
INSERT ... SELECT、REPLACE等隐式触发多行唯一校验的操作;优先用带ON DUPLICATE KEY UPDATE的单行插入
最易被忽略的一点:MySQL 8.0+ 的函数索引如果用于唯一约束,在 RC 下同样会触发间隙锁——只要它是唯一性校验路径的一部分,就逃不开。


















