普通索引和唯一索引的差别,不只是“能不能重复”。在 InnoDB 里,它们对写入路径的影响也不一样,关键就在 change buffer 能不能延迟合并二级索引页修改。

普通二级索引可以把部分修改先记下来,等页面读入 Buffer Pool 时再合并;唯一索引必须先判断是否冲突,不能简单把检查推迟。这个差异会直接影响写多读少场景下的成本。

先把机制边界说清楚

change buffer 缓冲的是普通二级索引页的修改。当目标索引页不在 Buffer Pool 里时,InnoDB 可以先记录变更,等以后读到这个页或后台合并时再改真实索引页。唯一索引必须先检查是否冲突,所以不能简单延迟。

整体路径

普通索引和唯一索引:change buffer 的取舍

上面这张图先看粗线条:宏观上,这是正确性和写入吞吐的取舍。普通索引允许先记账后合并,减少随机读;唯一索引必须先确认不存在重复值,很多时候需要把目标页读进内存。约束越强,写入前验证成本越高。

底层流程

普通索引和唯一索引:change buffer 的取舍:执行路径

底层拆解先看数据结构。「普通索引和唯一索引」至少涉及下面几类结构:

  • change buffer:保存在系统表空间中的变更缓冲结构。
  • 普通二级索引页:可以延迟合并。
  • 唯一二级索引页:写入前必须检查冲突。
  • merge 操作:读到页时把缓冲变更应用到真实页。

再看完整执行流程:

  1. 写入普通二级索引。
  2. 目标页不在 Buffer Pool。
  3. 记录到 change buffer 并提交。
  4. 后续读取该页或后台线程触发 merge。
  5. 如果是唯一索引,则必须先读页检查唯一性。

取舍与边界

版本差异上,早期 InnoDB 叫 insert buffer,后来扩展为 change buffer,支持 insert、delete mark、purge 等变化。MySQL 8.0 仍保留这个机制,但在 SSD 普及后,收益要结合真实 IO 成本重新评估。

change buffer 不是越大越好。写完马上读的场景会立刻触发 merge;大量缓冲积压会把成本推迟到后续查询;崩溃恢复时也要处理合并状态。SSD 环境下,减少随机读的收益可能低于复杂性成本。

典型问题:用机制化例子排查

写多读少场景下,普通索引可能借助 change buffer 延迟合并索引页修改;唯一索引必须先确认没有冲突,不能把检查留到以后。这就是两者写入成本不同的根源。

可以落到这些动作:

  • 业务天然唯一的字段必须用唯一索引,不要为了写入性能牺牲正确性。
  • 写多读少的普通二级索引更可能受益于 change buffer。
  • 监控 ibuf size、merge 次数和 Buffer Pool 命中,判断是否积压。
  • 写后立即读的链路要压测 merge 成本,不能只测写入。

收束:唯一性会改变写入路径

普通索引和唯一索引的差别不只在约束语义,也在写入路径。能不能使用 change buffer,本质上取决于这个变更能不能延迟检查。


关于十三Tech

我是十三,All in AI Agent 方向的架构师,专注 AI 工程实践。

我相信 AI 是程序员的最佳搭档,也希望帮助每一位开发者更好地驾驭 AI。

如果你想继续跟完这套「图解 MySQL」,欢迎关注公众号 「十三Tech」。后续会继续按机制、图解和实战排查这条线更新。

十三Tech公众号二维码