Change Buffer 是 InnoDB 用于缓存非唯一二级索引页变更的内存结构,仅在对应页未加载进 Buffer Pool 时生效,通过延迟合并(访问页、后台线程、淘汰前、关闭前)减少随机 I/O,提升写性能。

Change Buffer 不是写缓存,它只对非唯一二级索引的 DML 操作做延迟合并——前提是目标索引页当前不在 buffer pool 中。
Change Buffer 什么时候真正起作用
它只在满足全部以下条件时才介入:
-
INSERT、UPDATE或DELETE影响的是 非唯一二级索引(主键和UNIQUE索引绕过) - 被修改的索引页 当前未加载进 buffer pool(即不在内存中)
-
innodb_change_buffering配置未禁用对应操作类型(例如设为inserts时,UPDATE就不进 Change Buffer)
一旦页已在内存里,InnoDB 直接更新页,完全跳过 Change Buffer。SELECT 查询也从不触发合并——哪怕你刚 INSERT 一条带索引的记录,紧接着 SELECT WHERE indexed_col = ?,这次读会强制同步合并,但不是 SELECT 自身“用了”Change Buffer。
为什么非唯一索引才能走 Change Buffer
唯一性约束必须实时校验:插入或更新时,InnoDB 得立刻定位到对应索引页,检查是否已存在冲突值。这个过程无法延迟,所以 UNIQUE 索引和主键索引的变更永远直接读页、更新、刷 redo log。
而非唯一索引没有这种强一致性要求——多条相同键值可以共存,InnoDB 只需把变更暂存,等页后续被加载时再批量应用。这也是为什么日志表、流水表这类写多读少、索引键重复率高的场景最受益。
如何确认你的写入真正在用 Change Buffer
别靠猜测,看指标:
- 查当前缓存条目数:
SHOW STATUS LIKE 'Innodb_ibuf_size' - 查累计合并次数:
SHOW STATUS LIKE 'Innodb_ibuf_merges_insert'(还有_updates、_deletes) - 开详细监控:
SET GLOBAL innodb_monitor_enable = 'ibuf',再执行SHOW ENGINE INNODB STATUS,关注INSERT BUFFER AND ADAPTIVE HASH INDEX段
如果 Innodb_ibuf_size 长期为 0,或 Innodb_ibuf_merges_* 几乎不增长,说明你的 workload 没触发 Change Buffer——可能是 buffer pool 太大、数据集太小,或者索引全是唯一键。
调优前最容易被忽略的三件事
Change Buffer 是“加速器”,不是“万能开关”。真正影响效果的往往是更基础的设计:
- 表里有没有多余的非唯一二级索引?每个都是潜在的 Change Buffer 入口,也是后续合并的负担
-
innodb_change_buffer_max_size设得太高(比如 50),会挤占 buffer pool 真实热点页空间,反而加剧 LRU 淘汰 - 应用是否存在
INSERT后紧跟着SELECT的模式?这种“写后立刻读”会让 Change Buffer 的延迟优势失效,还增加单次查询延迟
它只在页确实不在内存、且后续又不会马上被读取时,才把随机 I/O 转成顺序写;否则,就是多一层内存拷贝加一次合并开销。


















