最有效优化是设innodb_autoinc_lock_mode=2且binlog_format=row,否则主从不一致;默认mode=1对insert select等仍持表级锁至语句结束,导致并发插入排队阻塞。

直接设为 2 是高并发写入场景下最有效的优化,但必须同时满足 binlog_format=ROW 且业务不依赖 ID 连续性,否则会引发主从不一致或逻辑错误。
为什么 innodb_autoinc_lock_mode=1(默认)仍会卡住
MySQL 8.0 默认值 innodb_autoinc_lock_mode=1 只对 INSERT INTO t VALUES () 类简单插入做了轻量优化,用 mutex 替代表锁;但只要语句含子查询、批量来源或混合模式,就会退化为语句级 AUTO-INC 表锁:
-
INSERT INTO t SELECT * FROM src、REPLACE INTO t SELECT、LOAD DATA全部触发表锁,锁持续到语句结束 - 多个事务同时执行这类语句时,会排队等待,
SHOW PROCESSLIST显示Waiting for table level lock -
SHOW ENGINE INNODB STATUS的 TRANSACTIONS 部分会出现AUTO_INC相关等待线程 - 即使只有一条这样的语句,也会阻塞其他所有 INSERT,QPS 上不去但 CPU 和 IO 并不高
innodb_autoinc_lock_mode=2 的真实行为和硬性前提
模式 2 是唯一真正跳过 AUTO-INC 锁的方案,靠全局计数器原子递增分配 ID,但它不是“改了就安全”:
- 必须设置
binlog_format=ROW,否则 MySQL 启动报错或降级警告;执行SELECT @@binlog_format确认返回ROW - 业务不能依赖 ID 连续性:不用 ID 做分页推算、不对外暴露为单号、没有代码假设
id + 1就是下一条 - 禁止混用显式指定自增字段与
ON DUPLICATE KEY UPDATE,例如INSERT INTO t (id, name) VALUES (NULL, 'x') ON DUPLICATE KEY UPDATE ...,会触发内部重试反而更慢 - ID 必然出现空洞:事务回滚后已预分配的 ID 不回收,
MAX(id)与实际行数差距可能很大
怎么验证 mode=2 是否真正生效
光看配置值不够,得结合运行时表现判断:
- 执行
SHOW VARIABLES LIKE 'innodb_autoinc_lock_mode'确认值为2,且SHOW VARIABLES LIKE 'binlog_format'返回ROW - 压测并发简单插入时,
SHOW ENGINE INNODB STATUS输出中不再出现AUTO-INC相关等待线程 - 查
information_schema.INNODB_METRICS中autoinc_wait_count计数器是否持续为 0 或极低 -
SHOW TABLE STATUS显示的Auto_increment值跳变是正常现象,不代表出错
不改参数也能绕开自增锁瓶颈的实操写法
参数只是辅助,真正卡住并发的是写入模式本身:
- 把大
INSERT SELECT拆成应用层分页 + 批量多值INSERT,例如每次插 500–1000 行:INSERT INTO t VALUES (),(),()... - 用
INSERT ON DUPLICATE KEY UPDATE替代REPLACE INTO,前者只在真正插入新行时才推进自增计数器 - 确保主键字段类型足够大(如
BIGINT UNSIGNED),避免空洞加速耗尽 ID 范围 - 监控空洞程度:
SELECT MAX(id) FROM t与SELECT COUNT(*) FROM t对比,差距过大需评估下游系统兼容性
真正容易被忽略的是:mode=2 下的 ID 分配不可预测,某些分库分表中间件依赖 ID 单调递增做路由,切换前务必确认兼容性;另外,应用层若写了“插入后立刻用刚生成的 ID 做关联查询”并假设它紧邻前一个 ID,这种逻辑会失效。











