← 返回题目列表

MySQL 自增主键是如何工作的?会不会连续?

高频 中等 第 13 / 28 题 更新于 2026/07/29
MySQL自增主键AUTO_INCREMENT

简化版

MySQL 自增主键由 AUTO_INCREMENT 生成递增值,适合 InnoDB 聚簇索引写入;但自增值不保证连续,事务回滚、插入失败、批量插入、重启和并发都可能造成空洞。

详细版

自增主键常见定义:

id BIGINT PRIMARY KEY AUTO_INCREMENT

插入时不指定 id,MySQL 会为新行分配下一个自增值。它的好处是短小、递增、写入友好,适合 InnoDB 聚簇索引。

但自增 id 不等于连续编号。比如插入时分配了 id=10,随后事务回滚,10 可能不会再被复用;插入失败或并发批量插入也可能消耗自增值。因此不能用自增 id 判断业务连续性,也不应把它当作订单号、票据号这类必须连续的编号。

完整版教学

一、自增主键解决什么问题

自增主键的目标是给每行生成一个简单唯一的数字标识。

CREATE TABLE users (
  id BIGINT PRIMARY KEY AUTO_INCREMENT,
  name VARCHAR(50)
);

INSERT INTO users(name) VALUES ('Alice'), ('Bob');

如果当前从 1 开始,可能得到:

id | name
1  | Alice
2  | Bob

它让应用不用手动生成单库唯一 id,也让 InnoDB 聚簇索引插入更接近顺序追加。

记忆钩子:自增 id 保证“递增生成”,不保证“无空洞连续”。

二、为什么自增主键适合 InnoDB

InnoDB 按主键组织聚簇索引。自增主键新值通常比旧值大,新记录更容易插入 B+ 树右侧。

已有 id:1,2,3,4
新插入 id=5 -> 追加到右侧
新插入 id=6 -> 继续追加

和随机 UUID 相比,自增主键更短、更有序,页分裂和随机写入压力通常更小。

这也是很多业务表默认使用 BIGINT AUTO_INCREMENT 的原因:简单、稳定、性能友好。

三、自增值为什么不保证连续

自增值分配后,可能因为事务回滚或插入失败而产生空洞。

示例:

START TRANSACTION;
INSERT INTO users(name) VALUES ('Tom'); -- 分配 id=10
ROLLBACK;

INSERT INTO users(name) VALUES ('Jerry'); -- 可能得到 id=11

结果中没有 id=10,这很正常。数据库更关心并发安全和生成效率,而不是让数字看起来连续。

可能造成空洞的场景:

场景是否可能产生空洞
事务回滚可能
唯一键冲突插入失败可能
批量插入预分配可能
删除历史数据一定会出现缺口

四、自增锁和并发插入

自增值生成需要并发控制,避免两个事务拿到同一个 id。InnoDB 有自增锁相关机制,不同配置和版本下锁策略有所差异。

直观理解:

事务 A 申请自增值 -> 得到 100
事务 B 申请自增值 -> 得到 101

对于简单插入,数据库可以较高效地分配 id;对于批量插入或 INSERT ... SELECT,可能需要预估或锁定更长时间。

面试里不必强背每种 lock mode 的细节,但要知道:自增主键在高并发写入下有专门机制保证唯一分配,同时可能出现空洞。

五、为什么不能把自增 id 当业务编号

很多业务编号要求连续、不可猜、带日期或满足审计规则,自增主键并不适合直接承担这些职责。

例如订单号如果直接使用自增 id:

10001, 10002, 10005

用户可能看到订单量,竞争对手也可能推测业务规模;空洞还可能让财务或运营误以为丢单。

更稳的做法是:主键用自增 id,业务订单号单独生成并加唯一索引。

id BIGINT PRIMARY KEY AUTO_INCREMENT,
order_no VARCHAR(64) NOT NULL,
UNIQUE KEY uk_order_no(order_no)

六、分布式场景下的限制

单库自增 id 简单好用,但分库分表后会遇到全局唯一和趋势递增问题。

常见处理方式:

不同库设置不同步长:库1 生成 1,3,5;库2 生成 2,4,6
号段模式:从中心服务批量取一段 id
雪花算法:按时间戳 + 机器号 + 序列生成趋势递增 id

如果系统明确会分库分表,提前规划 id 生成方案比后期迁移主键更轻松。

自增主键适合单库或单写入口,分布式扩展时要重新评估。

七、常见误区与追问

  • 误区:自增主键一定连续。 回滚、失败、删除、批量插入都可能产生空洞。
  • 误区:自增 id 可以直接当订单号。 它可预测且不保证连续,通常应和业务编号分离。
  • 误区:手动插入 id 永远没影响。 手动插入较大 id 可能推进后续自增值。
  • 误区:自增主键只影响唯一性。 在 InnoDB 中它还影响聚簇索引写入顺序和页分裂。
  • 追问:删除最大 id 后下次会复用吗? 通常不要依赖复用行为,应把自增值看作只增不保证填洞。
  • 追问:分库分表还能用自增吗? 可以通过步长、号段或全局 id 服务处理,但单库自增不再天然全局唯一。

八、加强记忆

自增主键回答要同时说好处和边界:好处是短小递增、单库唯一、InnoDB 写入友好;边界是不保证连续、不适合业务编号、分布式要重新设计。看到“会不会连续”这个追问,答案一定是否定的。