InnoDB中的Change Buffer是个啥?
Change Buffer 是作为InnotDB的一个优化技术,用于减少对MySQL的磁盘IO操作,从而提升磁盘性能。本文将从一个解决问题的角度来了解一下Change Buffer是个啥。
1. Change Buffer总览
众所周知,存储引擎瓶颈有很大一部分来源于对磁盘的随机IO操作。那么对于频繁的随机磁盘IO操作有哪些优化方案呢?
一般情况下,最常见的思路就是,直接加一层读缓存即可,此方案实现起来并不是很复杂。
但是,如果能让写请求也减少与磁盘的IO操作,那性能不是能再提升一步吗?没错,Change Buffer就是针对此方案的成果。整体的结构总览如下:

官方文档中,对Change Buffer的定义如下:
更改缓冲区是一种特殊的数据结构,当二级索引页不在缓冲池中时,它会缓存对二级索引页的更改。可能由INSERT、UPDATE或DELETE(DML)操作导致的缓冲更改,稍后在其他读取操作将页面加载到缓冲池时合并。
注意: 这里为什么声明了二级索引?不要着急,后面会解释。
接下来我们看看它的工作流程。
2. Change Buffer的工作流程
假定一个场景,要对在缓存中的页66做修改操作

过程如下:
- 直接修改缓冲池中的页;
- 写入redo log。
其中,第一步是在内存中操作;第二步属于磁盘的顺序写入。效率都很高。
在此过程中,减少了对磁盘的随机读和随机写的操作
另一个场景,要对不在缓存中的页72做修改操作

过程如下:
- 先把需要把从页72磁盘加载到缓冲池,一次磁盘随机读操作;
- 修改缓冲池中的页;
- 写入redo log。
在此操作中,减少了对磁盘的随机写操作
3. 会遇到哪些问题?
通过以上工作流程的一个简单认识,是可以看到很多比较棘手的问题。接下来进行一一探讨。
3.1 是否会出现一致性的问题?
在Change Buffer中,为了解决此问题,在读取操作会命中缓冲池的页, 然后进行merge。因此不会出现一致性的问题。
3.2 如果还没进行刷盘操作数据库就崩掉了怎么办?
为了解决这个问题,使用Redo log来保证数据的持久性。每次执行修改操作时,都会对redo log进行一个顺序写的操作。如果还没来得及刷盘数据库就崩了的话,重启后,还可以使用redo log进行恢复数据。
3.3 Change Buffer中的数据什么时候会刷入磁盘呢?
- 有一个后台线程,会认为数据库空闲时;
- 数据库缓冲池不够用时;
- 数据库正常关闭时;
- redo log写满时。
3.4 对于不在缓存池中的数据页修改后,是否会立即刷入磁盘?
不会,如果每次对于不在缓存中的页进行一次随机查询后,进行修改,再将其立即刷入磁盘,那么就又有一次随机写操作。而change buffer引入的目的有一点就是通过批量写入减少对磁盘的IO操作。立即刷盘的话,change buffer的引入就没有意义了。
3.5 为什么说Change Buffer只适用于非唯一普通索引页?
InnoDB里,聚集索引和普通索引(二级索引)存在异同。如果索引设置了唯一(Unique)属性,在进行修改操作时,InnoDB必须进行唯一性检查。也就是说,索引页即使不在缓冲池,磁盘上的页读取无法避免(否则怎么校验是否唯一!?),此时就应该直接把相应的页放入缓冲池再进行修改,而不应该再整写缓冲这个幺蛾子。
4. Change Buffer的工作原理
Change Buffer主要应用于非主键索引的更改操作,将即将应用到磁盘上的非主键索引页的更改暂存到内存中的缓冲区。
简单来说,Change Buffer是一个内存区域,用于存储即将应用到磁盘上的非主键索引页的更改。当一个非主键索引的记录被更新或删除时,这些更改不会立即被写回到磁盘上,而是先被暂存到Change Buffer中。这样做的目的是为了减少对磁盘的I/O操作,从而提高数据库的整体性能。
Change Buffer的执行过程可以分为以下几个步骤:
- 更改暂存: 当一个非主键索引的记录被更新或删除时,这些更改操作首先被暂存到Change Buffer中。Change Buffer使用特定的数据结构来记录更改操作的相关信息,如更改类型(插入、更新或删除)、更改的数据页地址以及更改的内容。
- 合并更改: 当数据页从磁盘上读取到内存中时,Change Buffer中的相关信息会被用来合并这些更改。这意味着,当从非主键索引页读取数据时,如果有相关的更改操作被暂存在Change Buffer中,这些更改会立即被应用到该页上。这样,读取的数据就包含了最新的更改,确保了数据的一致性。
- 刷新到磁盘: 虽然Change Buffer中的更改操作是暂存的,但它们最终还是需要被刷新到磁盘上以保持数据的一致性。在合适的时机,InnoDB会将Change Buffer中的更改操作写入到磁盘上的重做日志中。这一步是必要的,因为如果突然的系统故障或崩溃发生,这些未写回磁盘的更改可能会丢失。
- 清理过程: 随着时间的推移,Change Buffer中的数据可能会老化或不再需要。为了保持Change Buffer的使用效率,InnoDB会定期执行清理过程,移除那些已经过时或不再需要的更改操作信息。
当我们要更新一条普通索引记录的时候:
- 如果这条记录在内存中,那么直接更新内存;
- 如果该记录没有在内存中,那么就需要更新change buffer;
- 更新完 change buffer 之后,MySQL会在redo log中记录下change buffer 的修改;
- 事务就算完成了,后续binlog落盘,redo log commit;
- 当需要读取不在内存中的记录时,会将该数据页从磁盘加载到内存,然后应用change buffer中的修改,也就是merge操作。
5. 什么业务场景,适合开启InnoDB的写缓冲机制?
先说什么时候不适合,如上文分析,当:
- 数据库都是唯一索引;
- 或者,写入一个数据后,会立刻读取它;
这两类场景,在写操作进行时(进行后),本来就要进行进行页读取,本来相应页面就要入缓冲池,此时写缓存反倒成了负担,增加了复杂度。
什么时候适合使用写缓冲,如果:
- 数据库大部分是非唯一索引;
- 业务是写多读少,或者不是写后立刻读取;
可以使用写缓冲,将原本每次写入都需要进行磁盘IO的SQL,优化定期批量写磁盘。