MySQL 8.0重启后AUTO_INCREMENT不回溯,因其将已分配的最大自增值写入redo log,启动时重放日志并取MAX(id)与日志值较大者加步长作为新起点,彻底避免5.7的内存丢失问题。

MySQL 8.0重启后AUTO_INCREMENT不回溯,靠的是redo log写入
根本原因不是“变聪明了”,而是自增值变更被当作数据变更一样记进了redo log。MySQL 8.0启动时会重放redo log,从中提取出最后一次成功的自增计数器值,再跟表中当前SELECT MAX(id)比较,取较大者加步长作为新起点。
这意味着:即使你执行DELETE FROM t清空全表,只要之前插入过id=10000,重启后AUTO_INCREMENT仍是10001(假设auto_increment_increment=1),不会退回到1。
-
redo log里记录的是已成功分配并写入引擎层的值,事务回滚不影响它 - 崩溃恢复时,如果
redo log里有AUTO_INCREMENT=5000,而表里MAX(id)=4999,最终值就是5000;如果表里MAX(id)=5002,那最终值就是5003 - 该机制只对
InnoDB生效,MyISAM仍走老逻辑(但生产基本不用)
SHOW CREATE TABLE显示的AUTO_INCREMENT值是内存快照,不是权威值
执行SHOW CREATE TABLE t看到的AUTO_INCREMENT=xxx,是当前InnoDB字典缓存里的值,不是磁盘上持久化的“权威值”。它可能滞后于redo log里的最新分配值,尤其在高并发插入后还没触发检查点时。
真正可靠的参考是:SELECT AUTO_INCREMENT FROM information_schema.TABLES WHERE TABLE_SCHEMA='db_name' AND TABLE_NAME='tbl_name'。这个值由server层从数据字典读取,和重启后恢复逻辑一致。
- 刚插入一条
id=100,立刻SHOW CREATE TABLE看到AUTO_INCREMENT=101→ 正常 - 接着删除
id=100,再SHOW CREATE TABLE仍显示101→ 也正常,删除不改计数器 - 此时重启,恢复后的值仍是
101,不会变成100→ 和5.7行为本质区别
为什么唯一键冲突后ID仍递增,且不回滚
这是InnoDB设计上的主动取舍:避免锁竞争。当插入因Duplicate entry失败时,自增值已经由引擎层分配并写入redo log,回滚它需要额外加锁、校验、补偿,会严重拖慢高并发写入性能。
所以MySQL 8.0延续了这一策略——“分配即生效”,哪怕语句最终失败。这也是你看到ID不连续的最常见原因。
- 执行
INSERT INTO t (c) VALUES (2),其中c有唯一索引,且已有c=2 - InnoDB获取当前
AUTO_INCREMENT=100,设为新行id=100,并把100写入redo log - 检测到
c=2冲突,返回错误,但AUTO_INCREMENT计数器已升到101
升级后AUTO_INCREMENT看起来“跳变”或“偏低”,其实是旧状态被持久化了
这不是bug,是8.0持久化机制和你操作时序共同作用的结果。自增值只在INSERT(未显式指定主键)或ALTER TABLE t AUTO_INCREMENT = N时落盘。
如果表在5.7时代删过最大ID记录,又没触发INSERT,升级到8.0后首次启动仍会加载旧的持久化值(比如1000),哪怕当前MAX(id)是500。反之,如果升级前刚执行过ALTER TABLE t AUTO_INCREMENT = 1,那8.0就真从1开始——即使这会导致主键冲突。
- 没有“自动对齐
MAX(id)+1”逻辑,8.0宁可“跳”也不“退” -
TRUNCATE TABLE t会重置计数器为1,且不可回滚;DELETE FROM t+ALTER TABLE t AUTO_INCREMENT = 1组合极危险:中间若有并发INSERT,新写入的值会被覆盖 - 真正容易被忽略的是:升级前5.7表中残留的旧高水位,可能在8.0里被原样继承并持续生效


















