READ COMMITTED是唯一能实质性关闭常规间隙锁的隔离级别,但必须全局设置、配合ROW格式binlog并接受不可重复读;SET SESSION无效,因间隙锁启用取决于事务启动时的全局隔离级别,而非动态SQL判断。

READ COMMITTED 是唯一能实质性关闭间隙锁的隔离级别,但必须全局设置、配合 ROW 格式 binlog,并接受不可重复读——这不是开关,而是语义切换。
为什么 SET SESSION 无效?
间隙锁是否启用,取决于事务启动时的全局隔离级别,不是按 SQL 动态判断的。会话级设置 SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED 对已启动事务无效,也不会影响间隙锁行为。
- 必须执行
SET GLOBAL tx_isolation = 'READ-COMMITTED'(注意连字符写法) - 该设置在 MySQL 5.7+ 生效,但某些版本重启后失效,需写入
my.cnf持久化 - 已有连接不会继承新设置,必须断开重连或重启应用连接池
- 验证是否生效:运行
SELECT @@global.tx_isolation,返回值必须是READ-COMMITTED
binlog 必须设为 ROW 格式
READ COMMITTED 下,同一 SQL 在不同事务中可能看到不同快照,STATEMENT 或 MIXED 格式 binlog 无法还原这种非确定性行为,主从数据必然错乱。
- 检查当前格式:
SELECT @@binlog_format,必须返回ROW - 修改方式:在
my.cnf的[mysqld]段添加binlog_format = ROW - 修改后需重启 MySQL,且所有从库也必须同步设为 ROW,否则复制中断
- 注意:ROW 格式 binlog 体积更大,磁盘和网络带宽压力上升
外键和唯一索引仍会触发间隙锁
即使设为 READ COMMITTED,InnoDB 仍会在两类场景下加间隙锁:唯一约束冲突检测、外键引用完整性校验。这不是配置遗漏,而是底层一致性保障机制强制启用。
- 插入/更新违反主键或唯一索引时,例如
INSERT INTO t(id) VALUES(5),而表中已有 id=4 和 id=6,则 InnoDB 会锁住 (4,6) 防止其他事务插入相同值 - 存在外键时,父表上被子表引用的范围(如子表
user_id=100),父表DELETE FROM users WHERE id = 100可能触发相关间隙锁 - 这意味着你无法通过隔离级别“彻底”关掉所有间隙锁——它只是移除了业务中 90% 的常规间隙锁,但关键一致性路径仍保留
真正难处理的是关锁之后的事
关掉间隙锁不等于问题消失,而是把风险转移给了应用层:幻读不再由数据库兜底,主从一致性校验成本陡增,唯一冲突时的隐式锁行为也更难排查。
- 应用需自行保证「读-判-写」逻辑的原子性,比如用
SELECT ... FOR UPDATE+ 业务层重试,或改用INSERT ... ON DUPLICATE KEY UPDATE - 主从延迟突增时,不能简单归因为网络或 IO,要重点查是否因 READ COMMITTED 下 binlog 重放不确定性引发从库回滚重试
- 死锁日志里突然出现
lock_mode X locks gap before rec,说明某条 SQL 实际命中了唯一索引冲突路径——这种间隙锁不是配置没关干净,而是你没意识到它本就该存在


















