根本原因是默认innodb_autoinc_lock_mode=1下,insert select等批量语句全程持有auto_inc表级锁,导致并发insert排队;mode=2仅对insert values()无锁,且需binlog_format=row,否则主从不一致。

为什么你的 INSERT 总在 Waiting for table level lock
这不是慢查询,是自增锁卡住了。现象很具体:QPS 上不去、show processlist 里一堆 Waiting for table level lock、慢日志里单条 INSERT 耗时从几毫秒跳到几百毫秒甚至秒级。根本原因在于默认的 innodb_autoinc_lock_mode = 1:对 INSERT INTO t SELECT、REPLACE INTO t SELECT、LOAD DATA 这类语句,InnoDB 会全程持有表级 AUTO_INC 锁,直到整条语句执行完才释放——哪怕只插 100 行,其他所有并发 INSERT 都得排队。
innodb_autoinc_lock_mode=2 真的能关掉锁吗
能,但只对 INSERT INTO t VALUES () 这类 simple inserts 彻底无锁;INSERT INTO t SELECT 或 INSERT INTO t VALUES (1,'x'), (NULL,'y')(混合模式)仍会退化为语句级锁,开了也白开。
MySQL 9.6.0是面向Linux平台的2026年创新版本,核心架构迎来重大革新。其将外键约束与级联操作从InnoDB引擎层上移至SQL层,确保所有数据变更均被完整记录至Binlog,彻底解决了CDC(变更数据捕获)与主从复制中的数据不一致难题。此外,该版本引入container_aware启动选项以原生适配容器环境,并对审计日志进行了组件化重构,为追求极致数据一致性与云原生体验的开发者提供了全新选择。
-
innodb_autoinc_lock_mode = 2的真实行为是“预分配段”:事务启动时就向计数器批取 ID(比如一次拿 32 个),即使回滚也不回收,导致主键空洞(如刚插完 1001,下一条变成 1033) - 必须确认
binlog_format = ROW:执行SELECT @@binlog_format,不是ROW就别设,否则主从复制可能丢数据或错乱 -
SET GLOBAL innodb_autoinc_lock_mode = 2立即生效但重启失效,必须写进my.cnf的[mysqld]段才持久
不改参数,怎么绕过自增锁瓶颈
参数只是工具,真正压垮并发的常是写入模式本身。与其死磕锁,不如换写法:
- 把大
INSERT SELECT拆成小批量多值INSERT:每次 500–1000 行,用INSERT INTO t VALUES (),(),()替代全量SELECT - 避免
INSERT ON DUPLICATE KEY UPDATE在高频撞同一条唯一键(比如手机号)的场景下使用——它会持住二级索引记录的 X 锁和 insert intention 锁,形成锁队列 - 监控锁等待:查
performance_schema.data_lock_waits,看谁在等哪条记录的锁,而不是只盯着自增锁
最容易被忽略的三件事
调参前不验证这三点,线上大概率还是卡:
- 没查
SELECT @@binlog_format就设innodb_autoinc_lock_mode = 2→ 主从 ID 不一致 - 业务里硬编码了 “ID+1 就是下一条” 或用
id BETWEEN 1000 AND 1010做拉取 → mode=2 下跳号直接漏数据 - 误以为所有
INSERT都受益于 mode=2 → 实际只有VALUES ()类型无锁,其他仍卡










