update走change buffer仅当更新非唯一二级索引、对应索引页不在buffer pool中、事务已写redo log、配置允许且未禁用该功能时成立;主键或唯一索引更新必加载数据页,无法使用change buffer。

不是“需要先写入Change Buffer”,而是:只有满足特定条件时,InnoDB才允许把更新暂存在Change Buffer中——否则必须加载数据页到Buffer Pool再改。
什么情况下UPDATE会走Change Buffer?
Change Buffer只对非唯一二级索引的INSERT、UPDATE、DELETE生效,且必须同时满足:
- 目标索引页当前不在
Buffer Pool中(即内存里没有) - 该索引是
非唯一的(主键、唯一索引、唯一约束索引全部不适用) - 事务已成功写入
redo log(Change Buffer记录本身也受WAL保护) - 操作类型在
innodb_change_buffering配置范围内(默认all,但可被设为none或inserts等)
例如:UPDATE t SET name='x' WHERE id=123 —— 如果name字段上有非唯一索引,且该索引页未在Buffer Pool中,这次更新才会记入Change Buffer;而id是主键,其对应聚簇索引页必须加载进内存才能修改,不可能走Change Buffer。
为什么非唯一二级索引能跳过加载页?
核心原因是:它不需要立即做唯一性校验。主键和唯一索引每次插入/更新都得立刻读取目标页,检查是否已存在冲突值;而非唯一索引允许多行重复,InnoDB可以安全地把“加一条索引项”这个操作先缓存起来,等未来某次读请求把该页加载进来时,再合并(merge)进去。
这背后其实是权衡:
- 代价:多一次内存写(Change Buffer)、后续merge开销、查询首次命中该页时延迟略高
- 收益:避免一次随机磁盘IO(可能耗时几毫秒),尤其在写密集、索引页分散的场景下,I/O节省显著
注意:Change Buffer本身是Buffer Pool里划出来的一块内存区域(默认最多占25%),不是独立进程或外部存储——它仍是内存操作,只是换了个地方存变更。
常见误判和踩坑点
很多人看到“UPDATE走Change Buffer”就以为所有更新都能绕过内存加载,实际极易误判:
-
UPDATE语句哪怕只改一个字段,只要涉及主键或唯一索引列的值变更(如UPDATE t SET uid=100 WHERE id=1),就必然触发聚簇索引页加载,Change Buffer完全不参与 - 如果
innodb_change_buffer_max_size被设为0,或innodb_change_buffering设为none,Change Buffer功能被禁用,所有二级索引更新都会强制读页 - 执行
SELECT查到某行后马上UPDATE它,大概率该行所在的数据页已在Buffer Pool中,Change Buffer压根不会启用 -
SHOW ENGINE INNODB STATUS里的change buffer段能看到当前缓存大小和merge状态,但无法精确追踪某条UPDATE是否进了其中——它只统计总量
真正容易被忽略的是:Change Buffer不是“优化开关”,而是InnoDB在WAL+MVCC硬约束下,为非唯一二级索引量身定制的一条合法绕行路径。跳不过Buffer Pool,只是把“加载页→改索引”压缩成“记日志→延后merge”。











