MySQL 自增 ID 为什么不连续?事务回滚和并发插入会影响 AUTO_INCREMENT 吗?
下面是一段教学用的模拟面试。
🧑💻 面试官: MySQL 自增 ID 为什么会断号?
🙋♂️ 我: 可能是删除了中间的记录。
🧑💻 面试官: 一条记录都没删,插入以后回滚,也会出现空洞吗?
🙋♂️ 我: 分配出去的号码可能不会回收。
🧑💻 面试官: 那 ID 大的记录,就一定比 ID 小的记录晚提交吗?
自增 ID 表示分配出来的标识,不是连续的账本页码,也不是事务提交时间线。
面试速答(60 秒版)
InnoDB 的自增值分配与事务提交是两个过程。事务拿到号码后,即使回滚,已分配的自增值也不承诺回收,所以可以出现空洞。
失败或被忽略的插入、批量插入的预分配、显式写入较大编号,以及删除记录等,也可能影响我们观察到的编号。
并发时,先拿到 ID 的事务可以后提交,因此不能用 ID 大小证明提交顺序。不同自增锁模式对分配和并发处理有影响,但都不能把 ID 变成无空洞的业务序号。
实际设计里,把自增 ID 用作技术标识。如果业务要求严格连续编号,需要另外定义发号、失败与审计规则,不能靠重置 AUTO_INCREMENT 补齐。

图:自增 ID 不承诺连续。
知识点详解:号码发出去了,事务不一定完成
回滚为什么不把号码放回去?
假设下一个自增值是 101。事务 A 插入记录,拿到 101,随后回滚。下一次成功插入可能拿到 102,表里就没有 101。
号码分配与业务行的提交生命周期不同。InnoDB 不承诺把回滚掉的号码退回去复用。规则可以核对 MySQL 自增处理说明。
所以,看到编号空洞,不能直接推断“有人删了数据”,更不能把空洞数量当成丢失记录的数量。
分配顺序也不是提交顺序
假设 A 拿到 101,开始做较慢的后续工作;B 拿到 102,很快提交;A 最后才提交。
此时编号 102 的行,实际先完成事务。用 ID 排序可以表达一种编号顺序,但它不等同于严格的提交时间顺序。
查询“最新业务事件”时,要先明确最新指创建、发生、接收还是提交。一个字段不能替代所有时间语义。

图:先拿 ID,不等于先提交。
锁模式影响什么?
MySQL 8.4 中,innodb_autoinc_lock_mode 默认是 2,也就是 interleaved。它帮助提高并发分配能力,批量与并发场景可能产生交错。
其他模式有不同锁与分配规则,但不会给业务提供“所有已提交记录的编号严格连续”保证。失败、回滚和删除仍然是独立因素。
也不能把一条简单插入的观察结果,推成所有批量语句和并发事务的规则。
把空洞补回去,为什么危险?
如果有人已经拿着 ID 引用这条业务数据,复用或重编号可能造成关联错误。审计、外部系统和异步消息中的编号,也未必都在当前数据库里。
所以,不应为“看起来整齐”在生产里重置序列或搬动主键。技术标识允许空洞,通常是合理设计的一部分。
如果业务确实要求连续,例如某类正式凭证,就需要单独讨论何时确认编号、失败记录如何保留、撤销怎样审计,以及并发如何串行化。这是业务协议,不是自增字段的附赠功能。
怎样验证自己的理解?
在独立测试库里,分别执行分配后回滚、成功提交、并发交错和不同批量语句,记录拿号与提交时刻。
这些实验要在目标版本上运行。本文给的是规则与预期场景,没有把示意编号写成实际数据库日志。
面试官继续追问
COUNT(*) 能用最大 ID 代替吗?
不能。删除、回滚空洞和显式编号都让两者不同,最大编号不是记录数。
出现空洞是不是要报警?
先看业务约定。技术主键允许空洞,不应只因断号报警;真正的数据缺失,需要用完整性、日志或业务核对判断。
面试速记卡
- 分配与提交:是两件事。
- 回滚:已分配号码不承诺回收。
- 顺序:ID 顺序不是严格的提交顺序。
- 统计:最大 ID 不能代替记录数。
- 业务编号:连续性与审计规则需要另外设计。
公司面试真题
这道题暂未收录可核验的公司真题来源。你可以先阅读本文解析,或浏览已收录的公司面试真题。
浏览公司面试真题 →