change buffer只对非唯一索引生效,根本原因是唯一性校验必须实时完成,需立即加载索引页判断冲突,无法延迟合并;而普通索引允许重复,可先缓存变更再异步合并,减少随机io。

Change Buffer 为什么只对非唯一索引生效
根本原因在于唯一性校验必须实时完成。当执行 INSERT 或 UPDATE 时,InnoDB 需要确认目标值在索引中是否已存在——这要求完整加载对应索引页到 Buffer Pool,否则无法判断冲突。因此,PRIMARY KEY 和任何带 UNIQUE 约束的索引页修改,一律绕过 Change Buffer,直接走“读页→改页→写 redo”流程。
常见错误现象:误以为加了普通二级索引就能触发 Change Buffer,结果发现 SHOW ENGINE INNODB STATUS 中 change buffer 统计无增长——很可能该索引实际是 UNIQUE 的,或字段上隐式有唯一约束(如主键列被包含在联合索引中且前导列唯一)。
验证方式:
- 用
SELECT INDEX_NAME, NON_UNIQUE FROM INFORMATION_SCHEMA.STATISTICS WHERE TABLE_SCHEMA = 'db' AND TABLE_NAME = 't';查看索引是否为非唯一 - 检查建表语句中是否有
UNIQUE KEY或PRIMARY KEY覆盖了该索引字段
什么时候 Change Buffer 真正起作用:三个典型场景
Change Buffer 不是“自动开启就有效”,它依赖具体访问模式。以下场景下效果最明显:
-
写多读少的流水表:如订单日志、操作审计表,大量
INSERT写入,但索引页极少被后续SELECT触发加载 -
批量导入非主键索引数据:例如用
LOAD DATA INFILE或大批量INSERT ... VALUES (...), (...), ...,且目标表有多个非唯一二级索引 -
冷数据更新:更新某张大表中长期未被查询过的分区/范围,对应索引页大概率不在
Buffer Pool中
反例:高频更新+高频查询同一行(如用户余额表),索引页常驻内存,Change Buffer 几乎不被写入。
如何观察 Change Buffer 是否在工作
不能只看配置是否开启,得查运行时状态。关键指标来自 SHOW ENGINE INNODB STATUS\G 输出中的 INSERT BUFFER AND ADAPTIVE HASH INDEX 区块:
-
ibuf size:当前占用的段页数(不是字节数),值大于 0 表示有缓存内容 -
free list len:空闲页数量,越小说明缓存越满 -
merged operations各类统计(inserts/deletes/purges)反映合并频率
注意:innodb_change_buffer_max_size 默认为 25,表示最多占 Buffer Pool 的 25%;若业务写入激增但 ibuf size 始终卡在低位,可能是该参数太小,或写入根本没命中可缓存路径(比如全在唯一索引上)。
Change Buffer 合并(merge)的时机与风险点
Change Buffer 中的操作不会永远躺着,它会在几个明确时机被合并到实际数据页:
- 该索引页因
SELECT查询首次被加载进Buffer Pool时(最常见) - InnoDB 后台线程定期扫描(默认每秒一次),主动将部分缓存页加载并 merge
- 系统空闲时的
purge线程或关闭前的刷盘阶段
容易被忽略的风险:
- 合并过程本身消耗 CPU 和 I/O,若某次批量写入后立刻执行大范围
SELECT,可能触发集中 merge,造成短暂延迟尖峰 - 崩溃恢复时,
Change Buffer内容需从redo log重放,若innodb_fast_shutdown=2(默认),这部分未 merge 的变更会保留在磁盘的系统表空间中,重启后继续 merge,延长启动时间 -
innodb_change_buffering若设为none或inserts(旧版本兼容),则UPDATE/DELETE不进缓冲区,这点在升级后易被遗漏











