mysql 5.7 的 innodb_autoinc_lock_mode=1 在 insert ... select 等批量插入时卡住,因其预估行数后一次性加表级 auto-inc 锁至语句结束,阻塞其他插入;mysql 8.0 通过动态预分配、mdl 解耦和安全启用 mode=2 优化锁持有时间与并发性。

innodb_autoinc_lock_mode 参数在 MySQL 5.7 中默认为 1(“连续递增的轻量级锁”),但它在高并发批量插入场景下仍会成为性能瓶颈。根本原因不是“锁太重”,而是 ID 分配逻辑与事务生命周期耦合过紧——特别是 INSERT ... SELECT、REPLACE INTO、LOAD DATA 这类语句,即使只插几百行,也会触发表级 AUTO-INC 锁,阻塞其他插入线程。
MySQL 8.0 并没有“重写自增锁算法”,而是通过更精细的锁粒度 + 元数据锁分离 + 预分配策略优化了实际表现。升级本身不能自动解决瓶颈,必须配合配置调整和 SQL 写法变更。
为什么 MySQL 5.7 的 innodb_autoinc_lock_mode=1 在批量插入时卡住
-
INSERT INTO t1 SELECT ... FROM t2会预估行数并一次性加锁分配整段 ID,锁持续到语句结束,而非事务结束 - 即使
t2只有 10 行,InnoDB 仍按“最坏情况”申请锁,且无法被其他连接的单行INSERT抢占 - 多个并发
INSERT ... SELECT会形成锁队列,SHOW PROCESSLIST显示大量Waiting for table level lock或Waiting for auto-inc lock -
innodb_autoinc_lock_mode=2(无锁模式)在 5.7 中不可靠:主从复制可能因 binlog 顺序与执行顺序不一致导致 ID 冲突,尤其当sync_binlog != 1时
MySQL 8.0 的实际改进点不是“去掉锁”,而是减少锁持有时间
- 引入“动态预分配窗口”:对
INSERT ... SELECT,不再一次性锁整个范围,而是按需分批获取 ID 段(默认每批 64 个),释放 mutex 更快 -
AUTO-INC锁与元数据锁(MDL)解耦:避免因表结构变更等待引发的连锁阻塞 - 支持
innodb_autoinc_lock_mode=2安全启用:8.0 要求binlog_format=ROW+enforce_gtid_consistency=ON时,mode=2不再导致主从不一致 - 新增状态变量
Innodb_autoinc_readiness_wait,可监控自增就绪等待耗时,便于定位是否真由自增锁拖慢
升级 8.0 后必须做的三件事,否则性能可能更差
确认并显式设置
innodb_autoinc_lock_mode=2(仅当满足binlog_format=ROW且已开启 GTID);若未开 GTID,mode=1仍是安全底线,但需重构 SQL把批量插入拆成小批次:例如将
INSERT INTO t SELECT * FROM s改为循环执行INSERT INTO t SELECT * FROM s WHERE id BETWEEN ? AND ?,每次不超过 1000 行禁用可能导致隐式锁升级的操作:如避免在事务中混合使用
INSERT ... SELECT和UPDATE,防止自增锁被延长至事务提交点
真正卡住系统的,往往不是自增锁本身,而是它和其他锁(MDL、行锁、binlog commit 锁)形成的等待链。MySQL 8.0 的改进是让这条链变短、变松,而不是直接剪断——你得主动拆掉那些让它绷紧的写法。











