跳转到主内容
极星编程网:以代码为星,赴技术山海!

mysql如何优化自增主键竞争_mysql innodb_autoinc_lock_mode

INSERT ... SELECT 卡住其他插入是因为 innodb_autoinc_lock_mode=1 时升级为表级 AUTO-INC 锁,阻塞所有需新自增值的并发 INSERT;设为 2 可避免该锁但要求 binlog_format=ROW,且会导致自增 ID 空洞。 为什么 INSERT ... SELECT 会卡住其他插入 因为
innodb_autoinc_lock_mode=1
(默认)下,这类语句会升级为表级 AUTO-INC 锁,阻塞所有需要新自增值的并发 INSERT。不是锁行,是锁“下一个 ID 的分配权”。 典型场景:
INSERT INTO t2 SELECT * FROM t1
或带子查询的批量插入 只要语句长度不可预知(比如没指定具体值、也没用
VALUES
明确列出全部行),InnoDB 就不敢用轻量的 mutex,转而拿重锁 影响范围不限于同一张表——如果多个表共用同一个 auto-increment 缓存段(极少见),也可能间接干扰 怎么安全地调高
innodb_autoinc_lock_mode
设成
2
可彻底避免 AUTO-INC 表锁,但要求 binlog_format=ROW。否则主从不一致:语句模式下从库重放时无法复现主库分配的 ID 序列。 确认当前 binlog 格式:
SELECT @@binlog_format
,必须是
ROW
动态生效(重启不丢):
SET GLOBAL innodb_autoinc_lock_mode = 2
innodb_autoinc_lock_mode=0
是传统表锁模式,已基本淘汰,别用 注意:
INSERT ... ON DUPLICATE KEY UPDATE
和
REPLACE
在 mode 2 下仍可能触发间隙锁竞争,和 autoinc 无关,别混淆 自增 ID 空洞不是 bug,是 mode 2 的必然代价 设成
2
后,如果事务 A 预占了 100~199,事务 B 预占了 200~299,但 A 回滚了,那 100~199 就永远空着。这不是浪费,是并发安全的必要开销。 MySQL(Linux) MySQL 9.6.0是面向Linux平台的2026年创新版本,核心架构迎来重大革新。其将外键约束与级联操作从InnoDB引擎层上移至SQL层,确保所有数据变更均被完整记录至Binlog,彻底解决了CDC(变更数据捕获)与主从复制中的数据不一致难题。此外,该版本引入container_aware启动选项以原生适配容器环境,并对审计日志进行了组件化重构,为追求极致数据一致性与云原生体验的开发者提供了全新选择。 下载 空洞不影响查询、索引效率,InnoDB 不靠 ID 连续性做任何优化 如果业务强依赖“无跳号”(比如对外暴露 ID 当单号),就别碰
innodb_autoinc_lock_mode
,老实用
mode=1
+ 拆小批插入 监控空洞程度可用:
SELECT AUTO_INCREMENT FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA='db' AND TABLE_NAME='t'
对比实际最大 ID INSERT ... VALUES() 批量插入也踩坑? 单条
INSERT
插多行(如
INSERT INTO t VALUES (1),(2),(3)
)在
mode=1
下是安全的,用 mutex 分配 ID;但如果你写成三条独立
INSERT
,哪怕内容一样,就会触发三次锁申请——性能差,还容易锁等待。 能合尽量合:
INSERT INTO t VALUES (1),(2),(3),...,(1000)
—— 这种最多一次锁 超过 1000 行建议分批,每批 500~1000,避免单语句解析/网络传输压力过大 ORM 框架(如 Django ORM、MyBatis)默认可能拆成单行 INSERT,得查文档或抓包确认生成的 SQL 真正难处理的是混合场景:一边有长事务拿着 AUTO-INC 锁不放,一边大量短 INSERT 堵在后面。这时候看
SHOW ENGINE INNODB STATUS
的
TRANSACTIONS
部分,找状态为
LOCK WAIT
且 waiting for
auto-inc
的线程——它等的不是某一行,是整个表的 ID 发放权。

相关文章