当须要更新一个数据页时,若是数据页在内存中的话,则直接更新。而若是这个数据页不在内存中的话, InnoDB会把这些操做缓存在Change Buffer中。这样就不须要从磁盘读取数据了。在下次查询须要访问这个数据页的时候,将数据页读入内存,而后执行 change buffer 中与这个页有关的操做。经过这种方式就能保证这个数据逻辑的正确性。mysql
将 change buffer 中的操做应用到原数据页,获得最新结果的过程称为 merge。除了访问这个数据页会触发 merge 外,系统有后台线程会按期 merge。在数据库正常关闭(shutdown)的过程当中,也会执行 merge 操做。sql
显然,若是可以将更新操做先记录在 change buffer,减小读磁盘,语句的执行速度会获得明显的提高。并且,数据读入内存是须要占用 buffer pool 的,因此这种方式还可以避免占用内存,提升内存利用率。数据库
对于惟一索引来讲,全部的更新操做都要先判断这个操做是否违反惟一性约束。好比,要插入 (4,400) 这个记录,就要先判断如今表中是否已经存在 k=4 的记录,而这必需要将数据页读入内存才能判断。若是都已经读入到内存了,那直接更新内存会更快,就不必使用 change buffer 了。缓存
若是要在这张表中插入一个新记录 (4,400) 的话,InnoDB 的处理流程是怎样的。bash
第一种状况:性能
要插入的数据页在内存中,又分为两种状况spa
第二种状况:线程
将数据从磁盘读入内存涉及随机 IO 的访问,是数据库里面成本最高的操做之一。change buffer 由于减小了随机磁盘访问,因此对更新性能的提高是会很明显的。3d
经过上面的分析,你已经清楚了使用 change buffer 对更新过程的加速做用,也清楚了 change buffer 只限于用在普通索引的场景下,而不适用于惟一索引。那么,如今有一个问题就是:普通索引的全部场景,使用 change buffer 均可以起到加速做用吗?日志
由于 merge 的时候是真正进行数据更新的时刻,而 change buffer 的主要目的就是将记录的变动动做缓存下来,因此在一个数据页作 merge 以前,change buffer 记录的变动越多(也就是这个页面上要更新的次数越多),收益就越大。所以,对于写多读少的业务来讲,页面在写完之后立刻被访问到的几率比较小,此时 change buffer 的使用效果最好。这种业务模型常见的就是帐单类、日志类的系统。
反过来,假设一个业务的更新模式是写入以后立刻会作查询,那么即便知足了条件,将更新先记录在 change buffer,但以后因为立刻要访问这个数据页,会当即触发 merge 过程。这样随机访问 IO 的次数不会减小,反而增长了 change buffer 的维护代价。因此,对于这种业务模式来讲,change buffer 反而起到了反作用。
假设须要在表上执行以下语句
mysql> insert into t(id,k) values(id1,k1),(id2,k2);
复制代码
这里,咱们假设当前 k 索引树的状态,查找到位置后,k1 所在的数据页在内存 (InnoDB buffer pool) 中,k2 所在的数据页不在内存中。如图 2 所示是带 change buffer 的更新状态图。
这条更新语句作了以下的操做(按照图中的数字顺序): Page 1 在内存中,直接更新内存;
age 2 没有在内存中,就在内存的 change buffer 区域,记录下“我要往 Page 2 插入一行”这个信息将上述两个动做记入 redo log 中
同时,图中的两个虚线箭头,是后台操做,不影响更新的响应时间。
那在这以后的读请求,要怎么处理呢?好比,咱们如今要执行 select * from t where k in (k1, k2)。
读 Page 1 的时候,直接从内存返回。有几位同窗在前面文章的评论中问到,WAL 以后若是读数据,是否是必定要读盘,是否是必定要从 redo log 里面把数据更新之后才能够返回?实际上是不用的。你能够看一下图 3 的这个状态,虽然磁盘上仍是以前的数据,可是这里直接从内存返回结果,结果是正确的。要读 Page 2 的时候,须要把 Page 2 从磁盘读入内存中,而后应用 change buffer 里面的操做日志,生成一个正确的版本并返回结果。能够看到,直到须要读 Page 2 的时候,这个数据页才会被读入内存。
redo log 主要节省的是随机写磁盘的 IO 消耗(转成顺序写),而 change buffer 主要节省的则是随机读磁盘的 IO 消耗。