UPDATE走Change Buffer需同时满足:①更新字段属于非唯一二级索引;②对应索引页不在Buffer Pool中;③innodb_change_buffering未禁用update类型;否则直接读盘修改。

Change Buffer 只对非唯一二级索引的 UPDATE 生效,且仅当目标索引页不在 Buffer Pool 中时才介入;主键、唯一索引、聚簇索引的更新完全绕过它。
什么情况下 UPDATE 会走 Change Buffer?
必须同时满足以下三个条件:
- 被更新的字段涉及**非唯一二级索引**(例如普通
INDEX status,而非UNIQUE INDEX email) - 该索引对应的数据页当前**不在
Buffer Pool中**(即内存未缓存) -
innodb_change_buffering配置未禁用update类型(默认值all包含它)
只要其中任意一条不成立——比如你更新的是主键列、或该索引页刚被 SELECT 加载进内存、或配置设为 innodb_change_buffering = inserts——InnoDB 就会跳过 Change Buffer,直接加载索引页并原地修改。
UPDATE 走 Change Buffer 的实际行为
假设执行 UPDATE orders SET status = 'shipped' WHERE order_id = 123,且 status 是非唯一索引:
- 若
status索引中包含order_id = 123的那个页(比如页号0x1a7f)不在Buffer Pool,InnoDB 不会读盘,而是把这条“将order_id=123对应的status值改为shipped”的操作记录进 Change Buffer 内存结构中 - 该记录会立刻写入
redo log,保证崩溃可恢复;但**不会触发磁盘随机读**,事务即可返回成功 - 后续若某次
SELECT ... WHERE status = 'shipped'把这个页加载进内存,InnoDB 会在合并(merge)阶段把 Change Buffer 里的更新应用到该页上
注意:Change Buffer 不存储整行数据,只存必要信息——表 ID、索引 ID、操作类型、主键值(用于定位)、新旧索引字段值等。
为什么唯一索引的 UPDATE 绝对不走 Change Buffer?
核心原因是**唯一性校验无法延迟**:
- 更新唯一索引项前,InnoDB 必须确认新值是否已存在(比如
UPDATE users SET email = 'new@x.com' WHERE id = 5) - 这个判断依赖完整索引页内容,不能靠缓存的变更推断;否则可能漏检冲突,破坏约束
- 所以哪怕页不在内存,也必须先从磁盘读入、再检查、再更新——强制走一次随机 I/O
这也是你在慢日志里看到某些 UPDATE 突然变慢的常见原因:表面看只是改一个普通字段,但它恰好属于唯一索引,且对应页冷数据未缓存。
容易被忽略的副作用和调试点
Change Buffer 虽然加速写入,但会带来两个隐性成本:
-
Merge过程是后台异步做的,但一旦触发(比如某次查询加载了大量冷索引页),会瞬间放大 I/O 压力,拖慢其他查询——监控Innodb_buffer_pool_wait_free和Innodb_ibuf_merges状态变量很关键 - 如果业务长期只写不读,Change Buffer 积压过多,重启后首次访问相关索引会卡顿数秒甚至分钟,因为所有合并被迫在启动时集中完成
- 查看当前 Change Buffer 占用:运行
SHOW ENGINE INNODB STATUS\G,搜索INSERT BUFFER AND ADAPTIVE HASH INDEX部分,重点关注size和merges行
真正需要调优时,别只盯着 innodb_change_buffer_max_size,先确认你的 UPDATE 是否真的命中了 Change Buffer 场景——很多“以为能加速”的更新,其实根本没进去。


















