介绍
在 MySQL 5.5 之前,叫插入缓冲(Insert Buffer),只针对 Insert 做了优化,现在对 Delete 和 Update 也有效,叫做写缓冲(Change Buffer)。
它是一种应用在非唯一普通索引页(non-unique secondary index page)不在缓冲池中,对页进行了写操作,并不会立刻将磁盘页加载到缓冲池,而仅仅记录缓冲变更(Buffer Changes),等未来数据被读取时,再将数据合并(Merge)恢复到缓冲池中的技术。Change Buffer 的目的是降低写操作的磁盘 IO,提升数据库性能。
Change Buffer 是一个持久化的对象,存放在 ibdata1 文件中,同时也会写 redo log。
官方文档:https://dev.mysql.com/doc/refman/8.0/en/innodb-change-buffer.html
原理
执行流程
CREATE TABLE user (
id INT AUTO_INCREMENT, -- id 列是自增的
name VARCHAR(30), -- name 列是varchar
PRIMARY KEY(id) -- id 是主键
key(name) -- name 是非唯一二级索引
);
比如 user 表新增一条数据,对主键索引和非唯一二级索引会执行不同的存储步骤:
- 对于主键(id 列),每次插入都要立即插入对应的聚集索引页中(在内存中就直接插入,不在内存就先读取到内存)。
- 对于非唯一二级索引(secondary index)(name 列)
- 在没有 Change Buffer 之前,每次插入一条记录,就要读取一次页(读取内存,或者从磁盘读到内存),然后将记录插入到页中。
- 在有 Change Buffer 后,当插入一条记录时,先判断记录对应要插入的二级索引(secondary index)页是否在 Buffer Pool 中:
- 如果该二级索引(secondary index)页已经在 Buffer Pool 中,则直接插入。
- 反之,先将其 Cache 起来,放到 Change Buffer 中,等到该二级索引(secondary index)页被读到时,将 Change Buffer 中该页对应的记录合并(Merge)进去,从而减少 I/O 操作次数。
Change Buffer 就是用来减少 I/O 操作次数,提升二级索引插入的性能 。
Change Buffer 其实就是使用空间换时间,利用批量插入的方式减少 I/O 操作次数,因为 InnoDB 是索引组织表的存储方式,主键索引的叶子节点存放了完整的记录,二级索引可以先不着急插入,只需要先插入主键即可。
通过如下案例形象的描述 Change Buffer 带来的好处:
情况一:假如要修改页号为 4 的索引页,而这个页正好在缓冲池内。
如上图序号1-2:
- 直接修改缓冲池中的页,一次内存操作。
- 写入 redo log,一次磁盘顺序写操作。
这种情况的效率是最高的。
情况二:假如要修改页号为 40 的索引页,而这个页正好不在缓冲池内。
此时麻烦一点,如上图需要1-3:
- 先把需要为 40 的索引页,从磁盘加载到缓冲池,一次磁盘随机读操作。
- 修改缓冲池中的页,一次内存操作。
- 写入 redo log,一次磁盘顺序写操作。
没有命中缓冲池的时候,至少产生一次磁盘 I/O 操作。
InnoDB 在引入 Change Buffer 后,上文“情况二”流程会有什么变化?
假如要修改页号为 40 的索引页,而这个页正好不在缓冲池内。
引入 Change Buffer 优化后,流程优化为:
- 在写缓冲中记录这个操作,一次内存操作。
- 写入 redo log,一次磁盘顺序写操作。
其性能与这个索引页在缓冲池中相近。
稍后的一个时间,有请求查询索引页 40 的数据。
此时的流程如序号 1-3:
- 载入索引页,缓冲池未命中,这次磁盘IO不可避免。
- 从写缓冲读取相关信息。
- 恢复索引页,放到缓冲池 LRU 里。
哪些场景会触发 Merge 操作呢?
在执行 Merge 操作之前,Change Buffer 数据是存在内存中,为了防止数据库意外宕机导致数据丢失,系统会周期性将 Change Buffer 数据写入到共享表空间中。
辅助索引页被读取到 Buffer Pool 中
- 例如这在执行正常的 select 查询操作,索引页被调入内存,该索引页对应在 Change Buffer 中的索引更改记录就会发生 Merge 操作。
ibuf bitmap 页追踪到该辅助索引页已无可用空间时
Master Thread 工作
- 在 Master Thread 线程中每秒或每 10 秒会进行一次 Merge 的操作,不同之处在于每次进行 Merge 操作的页的数量不同。
MySQL 正常关闭时
redo log 写满时
- 几乎不会出现 redo log 写满,因为当 redo log 写满了,整个数据库将处于无法写入的不可用状态。
为什么写缓冲优化,仅适用于非唯一普通索引页呢?
如果索引设置了唯一(unique)属性,在进行修改操作时,InnoDB 必须进行唯一性检查。也就是说,索引页即使不在缓冲池中,磁盘上的页读取也无法避免,此时就应该直接把相应的页放入缓冲池再进行修改。
执行效果
通过如下命令可以查看 Change Buffer 的执行情况:
mysql> show engine innodb status\G
--------------省略其他输出-------------
-------------------------------------
INSERT BUFFER AND ADAPTIVE HASH INDEX
-------------------------------------
Ibuf: size 1, free list len 0, seg size 2, 0 merges -- 这里为0,可能是Buffer Pool足够大,
-- 数据页都缓存在内存中了,就用不到Change Buffer了
merged operations:
insert 0, delete mark 0, delete 0
discarded operations:
insert 0, delete mark 0, delete 0
- seg size:页的数量,例如当前页为 8K,则 seg_size * 8K 就是 Change Buffer 使用的内存大小。
- merges:合并了多少页。
- merged insert:插入了多少条记录。
- insert / merges:就是插入的效率(插入一条记录,就要读取一次页)。
- discarded operations:应该是很小的值,或者为 0,当记录写入到 Change Buffer 后,对应的表被删除了,则相应的 Buffer 中的记录就应该被丢弃。
使用 Change Buffer 的前提时,需要使用随机 IO ,这时才放入 Change Buffer 中,如果页已经在 Buffer Pool中,就不需要使用 Change Buffer 了。
举例说明 Change Buffer 的执行效果:
假设:merges = 10,insert = 1000,delete mark = 1000,delete = 1000
Merge 一次可以处理的记录数:(1000 + 1000 + 1000) / 10 = 300。
注意:如果 merges 的数据较大,说明 Change Buffer 调小了,也可能是非唯一索引建的多了。对表进行批量 IDU 的时候,可能会导致 Change Buffer 迅速增加。
参数
Change Buffer 有两个重要参数:
mysql> show variables like "%change_buffer%";
+-------------------------------+-------+
| Variable_name | Value |
+-------------------------------+-------+
| innodb_change_buffer_max_size | 25 |
| innodb_change_buffering | all |
+-------------------------------+-------+
2 rows in set (0.00 sec)
- innodb_change_buffer_max_size:配置 Change Buffer 的大小占整个缓冲池的比例,默认值是 25%,最大值是 50%。
- innodb_change_buffering:配置哪些写操作启用 Change Buffer,可以设置成 all、none、inserts、deletes、changes、purges。
| Value
| Description | | :—-: | :—-: | | all | The default value: buffer inserts, delete-marking operations, and purges. | | none | Do not buffer any operations. | | inserts | Buffer insert operations. | | deletes | Buffer delete-marking operations. | | changes | Buffer both inserts and delete-marking operations. | | purges | Buffer the physical deletion operations that happen in the background. |
性能
左图使开启了 Change Buffer,而右图未开启,一开始都比较高是因为还没有全量的进行刷磁盘(脏页全部在 Buffer Pool 中,还没有满),当执行 Merge 操作的时候,Insert 的性能下降,对比右图,开启 Change Buffer 后,Rows inserted per second 的值在 5k 左右,SSD 场景下也建议开启 Change Buffer。
参考
写缓冲(change buffer),这次彻底懂了!!!
InnoDB关键特性之change buffer
作者:殷建卫 链接:https://www.yuque.com/yinjianwei/vyrvkf/zytfa0 来源:殷建卫 - 架构笔记 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。