mysql 8.0 并未修复自增空洞问题,仅修复了重启后自增值重复的 bug;默认开启 innodb_autoinc_lock_mode=2 导致空洞更常见且不可预测,且空洞对性能无影响,无需人工干预。

MySQL 8.0 没有“修复”空洞问题,它只是修复了自增值重启丢失(重复)这个 bug,空洞本身仍是设计使然,且更常见了。
innodb_autoinc_lock_mode=2 默认开启,空洞反而更难预测
MySQL 8.0 将 innodb_autoinc_lock_mode 默认值从 1 改为 2(交错模式)。这不是为了填空洞,而是为提升并发写入性能:
- 模式 2 下,
INSERT INTO t SELECT ...或批量插入时,多个线程会各自预分配 ID 段(如 1001–1010、1011–1020),哪怕只插 1 行,剩下 ID 也永久废弃 - 同一语句内多个线程可能交错分配,导致 ID 序列在语句内部都不连续(比如插出 1001、1011、1002)
- 该模式要求 binlog 格式必须是
ROW,否则主从复制可能异常
自增计数器持久化 ≠ 连续性保证
MySQL 8.0 确实把自增计数器写入 redo log,实现重启不丢值 —— 但这只解决“重复利用旧 ID”的 bug(如 bug#199),不是让 ID 连续:
- 以前 5.7 及更早版本:重启后执行
SELECT MAX(id)+1重算,若刚删过最大 ID 行,就可能复用已用过的值,引发主键冲突 - 8.0 后:重启后恢复上次写入的计数器值,避免复用,但所有因回滚、
INSERT IGNORE、REPLACE或预分配产生的空洞照常存在 -
SHOW CREATE TABLE显示的AUTO_INCREMENT=xxx是当前计数器值,不是“下一个可用最小值”,它只管起点,不管中间有没有坑
哪些操作仍会稳定制造空洞(8.0 也一样)
这些行为在 8.0 中完全没变,空洞照跳不误:
-
INSERT IGNORE INTO t (c1) VALUES ('x'):唯一键冲突 → 报 warning + ID 自增 + 行未插入 → 空洞产生 -
INSERT INTO t SELECT ...:语句级预分配,哪怕只成功插入 1 行,整段 ID 都消耗掉 - 事务中插入后回滚:
INSERT执行即分配 ID,回滚不退还 - 显式指定 ID 插入:
INSERT INTO t (id, c1) VALUES (1000, 'x')→ 下一条自增从 1001 开始,前面全废
真正容易被忽略的是:你看到空洞想“修”,但人工重排(导出导入、ALTER TABLE ... AUTO_INCREMENT 设小值、触发器补号)会破坏外键、打乱主从 GTID、让应用缓存失效——而空洞本身对存储、查询、索引效率毫无影响。别为它停服务、锁表、写迁移脚本。











