自增跳号不是bug,而是innodb和sql server的正常行为;事务回滚、唯一键冲突、批量插入等均会触发预分配id且不回收,导致不可逆跳号,无法避免也不应修复。

自增跳号不是 bug,是 InnoDB 和 SQL Server 的正常行为;事务回滚一定会导致 ID 跳号,无法避免,也不该试图修复。
事务回滚后 AUTO_INCREMENT 为什么一定跳号
MySQL 的 AUTO_INCREMENT 值在语句执行「初期」就完成预分配,不是在提交时才写入。哪怕你执行了 ROLLBACK,那个 ID 已经被消耗掉了。
- 执行
BEGIN; INSERT INTO t VALUES (NULL, 'x'); ROLLBACK;后,SHOW CREATE TABLE t显示的AUTO_INCREMENT值已 +1 - 这个机制与
innodb_autoinc_lock_mode设置无关——只要用了NULL或没指定主键值,就触发预分配 - SQL Server 同理:
IDENTITY在语句解析阶段就取号,回滚不退还,且默认开启IDENTITY_CACHE(查sys.database_scoped_configurations可确认)
唯一键冲突也会悄悄吃掉 ID
很多人只盯着事务回滚,却忽略唯一索引冲突同样会触发 ID 预分配并丢弃。
- 建表含
UNIQUE KEY c(c),已有(1, 1, 1);再执行INSERT INTO t VALUES (NULL, 1, 2)→ 报错Duplicate entry '1' for key 'c',但AUTO_INCREMENT已升至 2 - 这种跳号无声无息,日志里只有
1062错误,容易漏查 - MySQL 8.0+ 可通过
performance_schema.table_io_waits_summary_by_table结合错误日志定位高频冲突点
批量插入(INSERT SELECT / LOAD DATA)放大跳号幅度
批量语句不是一行一分配,而是按“倍增策略”一次性预留多段 ID,断层更明显。
- 当前
AUTO_INCREMENT = 100,执行INSERT INTO t SELECT NULL, c, d FROM src LIMIT 5,最终可能跳到 108 或更高 - 这是为了减少锁竞争:InnoDB 第一次预占 1 个,第二次预占 2 个,第三次预占 4 个……呈指数增长
- 设
innodb_autoinc_lock_mode = 2(交错模式)可缓解,但不能消除;想完全避免,只能改用单条INSERT循环(性能代价大)或显式指定主键值
别碰 ALTER TABLE AUTO_INCREMENT = N 回拨
手动调低 AUTO_INCREMENT 值看似能“修复”跳号,实际埋雷。
-
ALTER TABLE t AUTO_INCREMENT = 5不检查表中是否已有id >= 5的行,极易引发主键冲突 - Navicat 还原 SQL 时若带了
AUTO_INCREMENT=12345,还原后立刻执行该语句,等于主动制造风险 - 真正安全的做法是:先查
SELECT IFNULL(MAX(id), 0) + 1 FROM t,再用结果赋值,且确保无并发写入
跳号本身不可逆,关键是要分清场景:如果只是展示序号(如列表行号),用 ROW_NUMBER() 窗口函数;如果业务强依赖连续编号(如发票号),必须放弃自增,改用序列或号段表控制——这点最容易被忽略,也最常引发线上争议。











