ChangeBuffer 是 InnoDB 缓存区的一种非凡的数据结构,当用户执行 SQL 对非惟一索引进行更改时,如果索引对应的数据页不在缓存中时,InnoDB 不会间接加载磁盘数据到缓存数据页中,而是缓存对这些更改操作。这些更改操作可能由插入、更新或删除操作 (DML) 触发。缓存区的更改操作会在磁盘数据被其它读操作加载到缓存中时合并到对应的缓存数据页中。
ChangeBuffer
InnoDB ChangeBuffer 的官网示意图如下所示,从图中能够看出以下信息:
- ChangeBuffer 用于存储 SQL 变更操作,比方 Insert/Update/Delete 等 SQL 语句;
- ChangeBuffer 中的每个变更操作都有其对应的数据页,并且该数据页未加载到缓存中;
- 当 ChangeBufferd 中变更操作对应的数据页加载到缓存中后,InnoDB 会把变更操作 Merge 到数据页上;
- InnoDB 会定期加载 ChangeBuffer 中操作对应的数据页到缓存中,并 Merge 变更操作;
基于集体了解并参考官网的 ChangeBuffer 示例图,我绘制了以下更为直观的的 ChangeBuffer 示例图:
ChangeBuffer 的作用
咱们晓得 InnoDB 举荐应用自增主键,插入时主键值时递增的,能够程序拜访。与聚簇索引不同,二级索引通常是不是惟一的,并且以绝对随机的程序插入。相似的,二级索引的更新和删除常常也会影响索引树中不相邻的二级索引数据页。
对于二级索引数据变更引起的随机拜访,如果每次都进行磁盘 IO 显然会影响数据库的性能。因而 InnoDB 不会立刻执行数据页不在缓存中的二级索引的变更操作,而是先将变更操作缓存起来,在某个时刻再将某一个数据页下面的所有变更操作合并到该数据页上,通过变更操作缓存 (ChangeBuffer) 可合并同一个数据页上的大量随机拜访 I /O。
ChangeBuffer 工作流程
变更操作什么时候放入 ChangeBuffer
并不是数据库中的所有操作都会进入 ChangeBuffer,满足以下条件的数据库语句,在执行阶段不会批改数据页,而是会进入 ChangeBuffer,
- SQL 会批改数据库中的数据;
- SQL 语句不波及惟一键的校验;
- SQL 语句不须要返回变更后的数据;
- 波及的数据页不在缓存中;
ChangeBuffer 合并到原数据页
咱们晓得,ChangeBuffer 中缓存了变更操作,这些操作最终须要合并到数据库的数据页,合并过程称为 Merge,那么在什么场景下会触发 ChangeBuffer 的 Merge 操作呢?
- 拜访变更操作对应的数据页;
- InnoDB 后盾定期 Merge;
- 数据库 BufferPool 空间有余;
- 数据库失常敞开时;
- RedoLog 写满时;
为什么 ChangeBuffer 只缓存非惟一索引数据
ChangeBuffer 仅仅实用于变更的数据未为非惟一索引的状况,如果变更操作批改的数据为惟一索引或者主键数据,那么 InnoDB 无奈把变更操作缓存到 ChangeBuffer,这是为什么呢?
以一张用户表为例,用户表蕴含主键 ID、年龄、姓名和性别四个字段,其中年龄增加了非惟一索引,初始数据及建表语句如下所示:
用户 ID | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
姓名 | 陈尔 | 张散 | 李思 | 王舞 | 赵流 | 孙期 | 周跋 | 吴酒 | 郑史 |
性别 | 男 | 男 | 女 | 女 | 男 | 男 | 男 | 女 | 男 |
年龄 | 5 | 10 | 20 | 28 | 35 | 56 | 25 | 80 | 90 |
create table user_info
(
id int primary key,
age int not null,
name varchar(16),
sex bool,
key(age)
)engine=InnoDB;
非惟一索引更新
假如咱们应用 SQL 语句 update user_info set age=6 where id=1
批改 ID= 1 的用户的年龄为 6,该操作会同时批改年龄索引以及行数据中的年龄,更新步骤如下:
- 如果须要更改的年龄索引页和行数据页在缓存中,间接更新缓存中的数据,并把数据页标记为脏页;
- 如果须要更改的年龄索引页和行数据页不在缓存中,间接把 SQL 语句
update user_info set age=6 where id=1
存储到 ChangeBuffer;
惟一索引更新
假如咱们应用 SQL 语句 update user_info set id=2 where id=1
批改 ID= 1 的用户的 ID 为 2,该操作会同时批改聚簇索引和行数据,更新步骤如下:
- 如果须要更改的聚簇索引和行数据页在缓存中,间接更新缓存中的数据,并把数据页标记为脏页;
- 如果须要更改的聚簇索引页和行数据页不在缓存中,须要把对应的数据页加载到缓存中,判断批改之后 ID 是不是合乎惟一键束缚,而后批改缓存中的数据;
能够看到,因为惟一索引须要进行唯一性校验,所以对惟一索引进行更新时必须将对应的数据页加载到缓存中进行校验,从而导致 ChangeBuffer 生效。
一般索引还是惟一索引
通过以上剖析,咱们晓得惟一索引无奈应用 ChangeBuffer,那么咱们理论应用过程中应该应用一般索引还是惟一索引呢?
从等值查问性能角度来看:
- 一般索引在查找到第一个满足条件的数据之后,须要持续向后查找满足条件的数据;
- 惟一索引在查找到第一个满足条件的数据之后,不须要再次向后查找,因为索引具备唯一性;
二者之间只相差一条记录,这个一条记录会带来多大的性能差距呢?答案是,微不足道。因为 InnoDB 引擎是以页为单位读取数据的,读取一条数据时,往往会将邻近的数据也读到内存,所以多向后查问几条数据带来的性能差异微不足道。
从索引批改角度来看:
因为非惟一索引无奈应用 ChangeBuffer,对索引的批改会引起大量的磁盘 IO,影响数据库性能。
综上可知,如果不是业务中要求数据库对某个字段做唯一性查看,咱们最好应用一般索引而不是惟一索引。
ChangeBuffer 实用场景
什么状况下 ChangeBuffer 会有较大的性能晋升呢?
- 数据库大部分索引是非惟一索引;
- 业务是写多读少,或者不是写后立即读取;
不适宜应用 ChangeBuffer 的场景与之对应:
先说什么时候不适宜,如上文剖析,当:
- 数据库都是惟一索引;
- 写入数据后,会立即读取;
ChangeBuffer 相干参数
innodb_change_buffer_max_size
: 配置写缓冲的大小,占整个缓冲池的比例,默认值是 25%,最大值是 50%。
写多读少的业务,才须要调大这个值。innodb_change_buffering
: 配置哪些写操作启用写缓冲,能够设置成 all/none/inserts/deletes 等。
我是御狐神,欢送大家关注我的微信公众号:wzm2zsd
本文最先公布至微信公众号,版权所有,禁止转载!