innodb_locks_unsafe_for_binlog 是 mysql 5.6 及更早版本中用于禁用间隙锁的参数,但自 5.7 起废弃、8.0 完全移除,现无效且启动会警告;避免间隙锁应依赖唯一索引等值查询、优化索引与 sql 写法,而非该参数。

innodb_locks_unsafe_for_binlog 是什么,现在还该用吗
这个配置项在 MySQL 5.6 及更早版本中存在,作用是关闭 Gap Lock(间隙锁),让 InnoDB 在可重复读(RR)隔离级别下退化为类似读已提交(RC)的锁行为。但自 MySQL 5.7 起,它已被废弃;MySQL 8.0 完全移除。如果你在配置文件里还写了 innodb_locks_unsafe_for_binlog=1,启动时会报警告,且不生效。
真正影响锁行为的是事务隔离级别本身,以及是否启用唯一索引、查询条件是否命中索引等。别再试图靠这个参数“绕过”间隙锁——它已经没用了。
怎么让 RR 级别下不加 Gap Lock
Gap Lock 不是由某个开关控制的,而是由「隔离级别 + 索引类型 + 查询条件」共同决定的。想避免 Gap Lock,核心是让 InnoDB 能精确锁定记录,而不是范围。
- 用唯一索引(含主键)做等值查询:比如
SELECT * FROM t WHERE id = 10 FOR UPDATE,只锁住 id=10 这一行,不锁间隙 - 避免范围查询:
WHERE id > 10或WHERE name LIKE 'a%'在非唯一索引上大概率触发 Gap Lock - 确认查询走了索引:用
EXPLAIN看type是否为const/eq_ref/ref;如果显示ALL或index,很可能锁整段索引区间 - 不要在 RR 下依赖“不加间隙锁”来提升并发:这容易引发幻读,尤其在有 INSERT 场景时
为什么改隔离级别到 READ-COMMITTED 也不总能禁掉 Gap Lock
RC 确实不加 Gap Lock,但有个关键例外:外键约束检查和唯一键冲突检测时,InnoDB 仍会加间隙锁防止并发插入破坏约束。比如两个事务同时 INSERT 相同唯一值,其中一个必须等另一个提交或回滚,背后就是间隙锁在起作用。
所以即使你把 session 设为 SET TRANSACTION ISOLATION LEVEL READ COMMITTED,只要表上有 UNIQUE KEY 或 FOREIGN KEY,某些 INSERT/UPDATE 仍可能被阻塞。
- 检查是否触发唯一约束锁:看
SHOW ENGINE INNODB STATUS中的TRANSACTIONS部分,留意lock_mode X locks gap before rec这类描述 - 临时去掉唯一索引测试(仅限排查):如果去掉唯一约束后阻塞消失,基本可定位是约束引发的隐式间隙锁
- RC 下仍需注意半一致性读(semi-consistent read)行为:UPDATE 不匹配的行会先返回旧值再放弃,不影响其他事务,但逻辑上不是“完全无锁”
线上调锁策略,优先看什么
别一上来就改隔离级别或幻想关掉某种锁。真实瓶颈往往出在 SQL 写法和索引设计上。
- 先查慢日志和
INFORMATION_SCHEMA.INNODB_TRX,确认哪些事务长期持有锁、等在哪儿 - 用
SELECT * FROM performance_schema.data_locks(MySQL 5.7+)看当前每个事务具体锁了哪些记录和间隙 - 重点审查带
FOR UPDATE或LOCK IN SHARE MODE的 SELECT,尤其是没走索引或用了OR/NOT IN/ 函数索引的语句 - 批量更新尽量拆小:一条
UPDATE ... WHERE id BETWEEN 1 AND 10000可能锁住上万个间隙;改成按主键分页执行,每次 100 行,锁粒度可控得多
锁的问题从来不是单点参数能解开的,它暴露的是数据访问模式和并发设计的真实水位。越想绕开,越容易在幻读、死锁或主从不一致上栽跟头。











