mysql自增id不连续是innodb预分配机制所致:insert解析阶段即申请并消耗id,无论事务回滚、唯一键冲突或on duplicate key update,id均不退还;批量插入和innodb_autoinc_lock_mode=1进一步加剧跳号。

INSERT失败时自增ID仍递增是InnoDB的预分配行为
MySQL的AUTO_INCREMENT不是“按需分配”,而是在语句解析阶段就向计数器申请下一个值。只要执行了INSERT(哪怕因主键或唯一键冲突失败),该ID就被消耗,不会退还。这不是bug,是InnoDB为并发安全做的设计取舍。
唯一键冲突和主键冲突触发相同预分配逻辑
错误类型不影响ID是否被占用:Duplicate entry报错来自唯一约束检查,但检查发生前ID早已分配。常见触发点包括:
-
INSERT INTO t (name) VALUES ('x')→ 冲突字段(如email)已存在 → 报ERROR 1062,但AUTO_INCREMENT已+1 -
INSERT IGNORE INTO t ...→ 失败不报错,但ID照常递增 -
INSERT ... ON DUPLICATE KEY UPDATE ...→ 即使走UPDATE分支,ID也已被预占
innodb_autoinc_lock_mode=1加剧跳号现象
MySQL 5.7+默认启用该模式,对INSERT INTO t SELECT ...等批量操作采用“预留式分配”:当前计数器为1000,执行INSERT INTO t SELECT ... LIMIT 100时可能直接跳到1064甚至更高。这不是错误,而是为避免行锁争用做的性能优化——但代价就是ID更不连续。
ALTER TABLE AUTO_INCREMENT = N不能填补空缺
这个语句只重设下一条插入的起点,不修复历史断号。关键限制:
-
N必须严格大于当前MAX(id),否则下次无显式ID的INSERT会报ERROR 1062 - 执行时对InnoDB表加写锁,大表慎用
- 主从环境下,仅改主库会导致从库
AUTO_INCREMENT值落后,后续同步可能隐性冲突
AUTO_INCREMENT当序列号用,却没意识到它本质是“轻量级唯一标识生成器”——连续性从来不在其设计目标里。











