change buffer不能“立即命中”索引页,因为它不缓存页内容,只记录“对哪个页做什么修改”;其触发依赖update时目标为非唯一二级索引且对应页不在buffer pool中,否则走常规读页修改路径。

Change Buffer 为什么不能“立即命中”索引页
Change Buffer 不是缓存数据页的“副本”,它根本没存页内容,只存“要对哪个页做什么修改”。所以不存在“命中页”的概念——它压根不参与页加载判断,只在 update_row 执行时被引擎内部触发写入。
UPDATE 触发 Change Buffer 的真实条件
不是所有 UPDATE 都走 Change Buffer。InnoDB 在调用 ha_innobase::update_row 时会做两个硬性检查:
- 目标索引必须是非唯一二级索引(
UNIQUE KEY或主键索引直接跳过) - 该索引对应的数据页当前不在 Buffer Pool 中(即
buf_pool->page_hash查不到)
只要其中任一条件不满足,InnoDB 就会走常规路径:把页从磁盘读进内存,再修改。这时候你看到的“快”,其实是 Buffer Pool 命中了,跟 Change Buffer 无关。
常见误判场景:你以为走了 Change Buffer,其实没有
很多同学看到 UPDATE ... WHERE non_unique_col = ? 就默认进了 Change Buffer,但实际常踩这些坑:
- 表刚启动或 Buffer Pool 刚清空,大量页还在磁盘上 → 确实走 Change Buffer
- 同一索引列被高频查询(比如
SELECT ... WHERE category = ?),导致对应页常驻内存 → 后续 UPDATE 全部绕过 Change Buffer - 执行
UPDATE前刚做过INSERT同一索引值,InnoDB 可能已顺带把页预热进 Buffer Pool → Change Buffer 被跳过 - 开启了
innodb_change_buffering = none或设为inserts却执行的是UPDATE→ DELETE-MARK 操作被禁用
如何验证某次 UPDATE 是否真进了 Change Buffer
不能靠慢日志或执行时间猜。真正可靠的观测点只有两个:
- 查
SHOW STATUS LIKE 'Innodb_buffer_pool_read_requests'和Innodb_buffer_pool_reads:如果后者没涨,而前者涨了,说明有页没从磁盘读——大概率变更进了 Change Buffer - 看
INFORMATION_SCHEMA.INNODB_METRICS表:change_buffer_merge_operations数值在后续查询触发 merge 时才增加,不是 DML 执行时就变
最易被忽略的一点:Change Buffer 的“生效”是异步的,且依赖后续读操作触发 merge。一次 UPDATE 返回成功,不代表索引页已被修改——它可能还在 Change Buffer 里躺着,等下一次 SELECT 或后台线程来唤醒。











