登录
推荐 文章 Go 技术 课程 下载 专题 AI
首页 >  数据库 >  MySQL

解释 interleaved 模式下并发语句预分配自增值和编号间隔的关系

来源:17golang原创

时间:2026-11-05 11:55:29 295浏览 收藏

先给结论:innodb_autoinc_lock_mode=2(interleaved)允许多个 INSERT-like 语句同时申请自增值,所以编号在不同语句之间交错并不奇怪。它保证的是编号唯一且总体单调递增,不保证每一条并发语句拿到的编号连续;当语句是 bulk insert 时,还可能因为预估和并发分配留下间隔。

这里的“间隔”是自增列的存储结果,不等于数据丢失,也不适合拿来生成严格连续的业务单号。理解这一点后,再看锁模式、插入类型和事务结果,排查就不会混在一起。

先区分唯一、递增和连续

三个词描述的是不同保证:

  • 唯一:不同并发语句不会拿到同一个自动生成值。
  • 单调递增:后分配的值不会小于先分配的值;这不代表查询结果按插入完成顺序排列。
  • 连续:某个语句或整张表的可见编号没有缺口,这是更强的要求。

interleaved 模式主要优化的是并发度。它不再让每个 INSERT-like 语句串行持有表级 AUTO-INC 锁,而是让多个语句同时参与编号分配。因此,“主键没有 100、103”不能直接推出“这一行插入失败”。

三种锁模式到底差在哪里

配置项是启动时的 innodb_autoinc_lock_mode,常见取值为 0、1、2:

模式核心行为更适合的判断
0 traditional所有 INSERT-like 语句使用表级 AUTO-INC 锁,语句内编号保持连续兼容旧语义、需要 statement-based replication 时优先考虑
1 consecutivebulk insert 使用表级锁,已知行数的 simple insert 只在分配阶段取轻量互斥希望兼顾并发和 statement-based replication
2 interleavedINSERT-like 语句不使用表级 AUTO-INC 锁,允许编号跨语句交错使用 row-based 或 mixed replication、优先并发吞吐时

注意:模式 2 是“允许并发分配”,不是“强制每条语句都产生间隔”。如果只有已知行数的 simple insert,单个语句通常仍不会出现间隔;bulk insert 的行数事先不完全确定时,风险才明显。

innodb_autoinc_lock_mode、AUTO-INC 分配器、simple insert、bulk insert、并发语句与编号连续性的关系图
图1:锁模式、插入形态与编号连续性的静态关系

为什么 bulk insert 会出现预分配间隔

以 INSERT ... SELECT 或 LOAD DATA 为例,InnoDB 在语句开始时可能不知道最终要插入多少行。并发模式下,分配器会在多个语句之间交错提供编号;当某个 bulk insert 先拿到一段估计区间、实际只使用其中一部分时,未使用的编号就不会回收给下一条语句。

可以用一个抽象场景理解,而不要把下面的数字当作固定复现结果:

-- 建立一个用于观察自增列的示例表;业务字段不参与编号分配
CREATE TABLE orders_demo (
    id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
    source_name VARCHAR(32) NOT NULL
) ENGINE = InnoDB;

-- bulk insert 的行数来自查询,分配器在处理过程中可能无法预先知道最终行数
INSERT INTO orders_demo (source_name)
SELECT source_name FROM import_stage;

-- 查看实际落盘的编号;结果重点看 id 是否有间隔,不要假设按提交顺序排列
SELECT id, source_name
FROM orders_demo
ORDER BY id;

如果另一条 INSERT-like 语句在第一条处理期间也申请编号,它拿到的值可能落在前一条语句最终写入的编号之间。于是你会看到“单条语句生成的行并不连续”,但所有已经分配的值仍然保持唯一和递增。

编号间隔要拆成三种原因

排查时建议分别问三个问题:

  1. 是否有并发 bulk insert? 如果有,interleaved 模式下跨语句交错和预分配间隔是正常可能性。
  2. 是否是 mixed-mode insert? 同一条多行 INSERT 同时显式提供部分自增值时,自动分配的数量可能超过实际使用数量。
  3. 是否发生过回滚? 只要自增值已经分配,即使语句或事务后来失败并回滚,这些编号也不会被复用。

第三种原因并非 interleaved 独有。传统模式和 consecutive 模式同样会因为回滚产生丢失的自增值;删除数据也会让查询结果看起来不连续,但那属于数据生命周期问题,不是分配器预留问题。

并发 bulk insert、预分配区间、语句未使用的编号、事务回滚、不复用编号与行数据中的间隔关系图
图2:预分配与回滚造成编号间隔的原因边界

复制模式决定能不能直接用 2

如果使用 statement-based replication,副本需要按语句重放并得到可预测的自增值;这时应在源库和副本使用 0 或 1,并保持配置一致。interleaved 允许并发语句交错,按语句重放时可能得到不同分配顺序,因此不适合这种复制方式。

如果使用 row-based replication,副本关注的是行变化而不是重新执行语句的顺序,三种锁模式都可以安全使用。mixed format 对不适合 statement-based 的语句会采用行方式,也可以按实际复制策略评估。

现象或约束先看什么判断方向
高并发导入吞吐优先是否有 row-based/mixed replication满足条件时考虑模式 2,接受编号不连续
必须按语句可预测地重放binlog_format 与副本配置选择模式 0 或 1,并保持源副本一致
业务要求连续票号是否把 AUTO_INCREMENT 当业务编号重新设计独立的业务序列,不要靠改锁模式补连续性

一条实用的判断口诀

看到自增列有空洞,先查“并发 bulk insert、mixed-mode、回滚、删除”四件事;看到 interleaved,不要只问“为什么不连续”,而要问“复制格式是否允许、业务是否真的需要连续”。

对普通主键来说,唯一性和并发吞吐往往比连续性更重要。只有当编号承担票据、发票或外部序号职责时,才应该单独设计可审计、可补偿的业务编号机制。

常见追问

interleaved 会让主键重复吗?

正常的 InnoDB 自增分配保证并发生成值不重复;间隔和重复是两个不同问题。若出现重复,应另查显式写入、数据类型上限或表结构约束。

把锁模式改成 0 能消除所有间隔吗?

不能。模式 0 主要保证语句分配的连续性,但回滚、删除以及显式混合插入仍可能留下间隔。

能否用 MAX(id)+1 生成连续编号?

不建议在并发写入中这样做。它会把编号生成变成需要额外锁和冲突处理的业务逻辑,不能替代 InnoDB 的自增机制。

声明:本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
相关阅读
更多>
最新阅读
更多>
课程推荐
更多>