MySQL自增主键不连续是常态,源于自增值预分配机制:执行INSERT前即+1,事务回滚或唯一键冲突均不退还,导致空洞;8.0起重启后自增值持久化,空洞更稳定。

MySQL 自增主键不连续是常态,不是 Bug;事务回滚只是其中一环,真正影响执行流的是「自增值预分配 + 非原子性」这个底层机制。
事务回滚时自增值已经+1了
MySQL 的 AUTO_INCREMENT 值在语句执行「前」就完成预分配,和 INSERT 是否成功无关。回滚只撤销数据写入和锁,不退还已分配的自增值。
- 执行
INSERT INTO t VALUES (NULL, 1, 1)时,InnoDB 先读当前AUTO_INCREMENT=5,立即设为6,再尝试插入 - 若因唯一键冲突或事务中途
ROLLBACK,行没写进去,但AUTO_INCREMENT已变成6 - 下次成功插入时,直接用
6,跳过5—— 空洞产生
不同 MySQL 版本对重启后自增值的处理差异
自增值是否“看起来更连续”,取决于它能否跨重启保留。这和事务回滚无关,但会影响你观察空洞的直观感受。
- MySQL ≤ 5.7:
AUTO_INCREMENT只存内存,重启后执行SELECT MAX(id) + 1 FROM t重算,删掉最大 ID 行再重启,值就变小 - MySQL ≥ 8.0:自增值变更记入
redo log,重启后能恢复到崩溃前最后分配的值,空洞“稳定存在”,不会被重算抹平 - 注意:
SHOW CREATE TABLE输出的AUTO_INCREMENT=xxx是当前内存值,不是磁盘持久值(8.0 后才是)
唯一键冲突比事务回滚更容易触发空洞
很多人以为只有显式 ROLLBACK 才会浪费自增 ID,其实隐式失败更常见——尤其是唯一索引冲突。
- 插入
(NULL, 1, 1)失败报ERROR 1062 (23000): Duplicate entry '1' for key 'c',自增值照样 +1 - 这种失败不涉及事务上下文,连
ROLLBACK都没有,但空洞照常产生 - 批量导入时若未做
INSERT IGNORE或ON DUPLICATE KEY UPDATE,每条冲突都吃一个 ID
真正难处理的不是“为什么断”,而是“断了之后业务逻辑是否依赖连续性”——比如用 ID 做分页游标、生成短链、校验数据范围。这些场景要提前规避,而不是指望 MySQL 把 ID 填满。











