MySQL在READ COMMITTED隔离级别下默认不使用间隙锁,仅加记录锁;若观察到类似间隙锁行为,多因配置错误、索引缺失、会话级隔离级别被覆盖或误判锁类型所致,真实间隙锁在RC下仅极少数约束检查场景短暂出现。

MySQL在READ COMMITTED(RC)隔离级别下默认不会使用间隙锁,如果你观察到间隙锁行为,大概率是配置异常、SQL写法触发了隐式锁升级,或误判了锁类型。
RC下间隙锁出现的典型误判场景
很多开发者看到SELECT ... FOR UPDATE阻塞了INSERT,就认为“有间隙锁”,其实更可能是以下原因:
- 实际执行的是
UPDATE或DELETE语句——这类当前读操作在RC下虽不加间隙锁,但若WHERE条件未命中任何记录,InnoDB仍可能对索引扫描路径上的间隙做轻量级锁定(非标准间隙锁,属实现细节,不可依赖) - 表中没有可用索引,导致优化器退化为全表扫描,此时InnoDB可能对整个聚簇索引范围加锁(表现类似间隙锁,实为表级锁定行为)
- 客户端或中间件显式设置了
transaction_isolation='REPEATABLE-READ',但你没注意到会话级配置已覆盖全局 - 执行了
SELECT ... LOCK IN SHARE MODE且条件落在唯一索引上——RC下该操作只加记录锁,但如果查询返回空结果,部分MySQL版本(如5.7.20前)存在间隙锁残留bug
验证当前是否真有间隙锁:用INFORMATION_SCHEMA检查
直接查INFORMATION_SCHEMA.INNODB_TRX和INFORMATION_SCHEMA.INNODB_LOCKS(MySQL 8.0+用performance_schema.data_locks)比凭现象猜测更可靠:
SELECT trx_id, trx_state, trx_isolation_level FROM INFORMATION_SCHEMA.INNODB_TRX WHERE trx_state = 'LOCK WAIT';
再关联锁信息时注意看LOCK_MODE字段:X,GAP或S,GAP才表示真正的间隙锁;X,REC_NOT_GAP是纯记录锁;X,NEXT_KEY才是临键锁(仅RR下常见)。
RC下间隙锁真正生效的极少数情况
严格来说,标准MySQL文档明确说明RC不启用间隙锁来防幻读。但以下两个边界情况可能触发间隙级锁定行为:
- 外键约束检查:即使RC级别,插入子表记录时若需校验父表是否存在对应值,InnoDB会对父表相关索引间隙加锁防止竞态
- 唯一索引冲突检测:当
INSERT ... ON DUPLICATE KEY UPDATE遇到唯一键冲突,InnoDB会在冲突值前后间隙加短暂锁,避免其他事务同时插入相同值造成死锁
这两种都不是为了防幻读,而是保证约束一致性与死锁规避,持续时间极短,且不对外暴露为可观察的“间隙锁等待”。
真正容易被忽略的是:autocommit=0下手动开启事务后,哪怕设了RC,只要后续语句触发了范围扫描+当前读,而索引设计又不合理(比如缺少覆盖索引),就可能让InnoDB被迫扩大锁范围——这不是间隙锁逻辑变了,而是它在“没得选”的情况下用最保守方式保数据安全。


















