change buffer是innodb中缓存对不在缓冲池中的非唯一二级索引页的insert、update、delete操作的数据结构,本质为b+树,仅在索引页未加载时生效,用于减少随机i/o。

Change Buffer 是什么:不是缓存数据,是缓存“变更操作”
Change Buffer 不是把索引页本身缓存起来,而是把对**不在 Buffer Pool 中的非唯一二级索引页**所做的 INSERT、UPDATE、DELETE 操作暂存在内存里。它本质是一棵 B+ 树,键是 (table_id, index_id, space_id, page_no),值是变更记录(比如插入哪条索引记录、删除哪个主键对应的索引项)。
关键点在于:它只作用于二级索引(非聚簇索引),且该索引页当前未被加载进 Buffer Pool。一旦页在内存中,InnoDB 就直接改页,不走 Change Buffer。
Change Buffer 失效的 4 种典型场景
它不是永远有效,以下情况会让变更立刻落盘或跳过缓存:
-
涉及唯一索引约束检查:比如对
UNIQUE KEY或PRIMARY KEY执行INSERT,InnoDB 必须立即读取对应索引页校验唯一性,无法延迟——此时强制加载页,innodb_change_buffering设置为all也无效 -
显式关闭变更缓存:全局或会话级设置
SET GLOBAL innodb_change_buffering = 'none',所有 DML 都绕过 Change Buffer -
索引页被提前加载:哪怕只是执行了一次
SELECT ... WHERE访问了该二级索引页,页就被读入 Buffer Pool,后续对该页的写操作就不再进入 Change Buffer -
后台合并触发强制刷出:当
innodb_change_buffer_max_size达到上限(默认 25% Buffer Pool),或空闲线程启动批量 merge,Change Buffer 中的记录会被主动合并并刷回磁盘——这时“缓存”就结束了
为什么你查 SHOW ENGINE INNODB STATUS 看不到预期的 Change Buffer 增长
常见误判是认为“写了大量非唯一索引就一定积压 Change Buffer”,但实际常被忽略的点有:
- 表刚启动时 Buffer Pool 空,Change Buffer 明显生效;但运行几小时后,热索引页大概率已驻留内存,后续写操作直接走常规路径
-
innodb_change_buffering默认为all,但如果你用的是 MySQL 5.6 之前版本,它可能只支持inserts(旧版叫 Insert Buffer),UPDATE/DELETE不生效 - 如果二级索引字段本身是
NOT NULL且有UNIQUE属性(哪怕没加UNIQUE约束,但字段值天然唯一),InnoDB 有时会保守处理,跳过缓存 - 使用
LOAD DATA INFILE批量导入时,若启用了innodb_table_locks=ON或锁表,部分版本会禁用 Change Buffer 以保证顺序一致性
真正依赖 Change Buffer 的业务,得满足三个硬条件
它不是通用加速器,而是高度场景化的优化机制:
- 索引必须是非唯一二级索引(
KEY idx_status (status)可以,UNIQUE KEY uk_email (email)不行) - 写入频率远高于该索引的查询频率(比如日志表每秒写 1000 条,但按
create_time查一次/分钟) - Buffer Pool 不足以缓存全部活跃索引页(否则页常驻内存,Change Buffer 形同虚设)
SSD + 大内存环境里,Change Buffer 的收益可能极低,甚至因额外内存开销和 merge 延迟反而拖慢读请求——这点容易被监控图表忽略,只看写 TPS 上升,却没盯住 merge 期间的 SELECT 延迟毛刺。











