概念描述
change buffer 更改缓冲区是一种特殊的数据结构,当这些页面不在缓冲池中时,它会缓存对二级索引页面的更改。可能是有DML操作(INSER\UPDATE\DELETE)产生了缓冲更改,将在以后其他读取操作将页面加载到缓冲池中时合并。
change buffer 原理
当执行写操作时,数据页存在于 Buffer Pool 中时,会直接修改数据页。如果数据页不存在于 Buffer Pool 中时,InooDB 会将这些更新操作缓存在 change buffer 中,这样就不需要从磁盘中读入这个数据页了。当下次访问到这条数据后,会把数据页加载到 buffer pool中,并且合并 change buffer 里面的变更。这样保证了数据的一致性,且批量写对性能影响不大。
将 change buffer 中的操作合并到原数据页,得到最新结果的过程称为 merge。
merge 触发的情况:
- 访问该数据页;
- 后台 master 线程会定期 merge;
- 数据库缓冲池不够用时;
- 数据库正常关闭时;
- redo log 写满时;
change buffer 是可以持久化的数据
- 当更新某个数据页时,该页在内存中,那么直接更新。
- 如果该页不在内存中,那么先将更新操作记录在 change buffer 中,这时不需要从磁盘中读出数据页。
- 将操作记录到 redo log 中,防止机器意外关闭导致数据丢失。(redo log 未commit的话,就要看 binlog 有无 commit 了,有 commit 的话就可以通过 binlog 恢复 redo log 再恢复 change buffer,未 commit 的话这部分数据可能会丢失)
- 这时已经可以返回给客户端更新成功了,因为即使机器意外重启,也可以通过 redo log 找回数据。
change buffer 和 redo log 对于磁盘的随机IO影响
- redo log 是减少随机写磁盘IO的消耗。每个操作先记录 redo log ,系统空闲时或 redo log 满时进行磁盘IO。
- change buffer 是减少随机读磁盘IO的消耗。更新时如果内存中不存在该数据页,也不需要马上进行磁盘IO,而是先记录在 change buffer 中,之后再 merge。
change buffer 为什么针对非唯一普通索引页
使用唯一索引,所有的更新操作都要先判断这个操作是否违反唯一性约束。而这必须要将数据页读入内存才能判断。如果已经读入到内存了,那直接更新内存会更快,就没必要使用 change buffer,因此,唯一索引的更新就不能使用 change buffer,实际上也只有普通索引可以使用。
测试验证
change buffer 相关参数
-
在InnoDB中,可以通过参数innodb_change_buffer_max_size变量允许将更改缓冲区的最大大小配置为缓冲池总大小的百分比,默认值是25,最大是50。例如当这个参数值是50 的时候,表示 change buffer 最多只能占用 buffer pool 的50%。
-
innodb_change_buffering 默认all表示所有的非唯一普通索引页写入都使用change buffer。
- all #默认值:缓冲区插入、删除标记操作和清除。
- none #关闭change buffer,不要缓冲任何操作。
- inserts #缓冲插入操作
- deletes #缓冲删除标记操作
- changes #缓冲插入和删除标记操作
- purges #缓冲在后台发生的物理删除操作
监视更改缓冲区
以下选项可用于更改缓冲区监视:
1. InnoDB 标准监视器输出包括更改缓冲区状态信息。要查看监视器数据,请发出该 SHOW ENGINE INNODB STATUS 语句。
mysql> SHOW ENGINE INNODB STATUS\G;
更改缓冲区状态信息位于 INSERT BUFFER AND ADAPTIVE HASH INDEX 标题下,类似于以下内容:
-------------------------------------
INSERT BUFFER AND ADAPTIVE HASH INDEX
-------------------------------------
Ibuf: size 1, free list len 0, seg size 2, 0 merges
merged operations:
insert 0, delete mark 0, delete 0
discarded operations:
insert 0, delete mark 0, delete 0
Hash table size 4425293, used cells 32, node heap has 1 buffer(s)
13577.57 hash searches/s, 202.47 non-hash searches/s
#size 1 正在使用的 page
#free list len 0 空闲的 page
#seg size 2 当前 change buffer 大小 2*16K
#0 merges 合并的数目
#insert 0 通过 change buffer 插入的数目
#delete mark 0 通过 change buffer 删除的数目
#delete 0 通过 change buffer purge 的数目
#change buffer 的效果 = merges/(insert + delete mark + delete) 结果越小说明 change buffer 对性能提升越有利
2. Information Schema INNODB_METRICS 表提供了在 Standard Monitor 输出中找到的大部分数据点 InnoDB 以及其他数据点。 要查看更改缓冲区指标和每个指标的描述,请发出以下查询:
mysql> SELECT NAME, COMMENT FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE NAME LIKE '%ibuf%'\G;
+-----------------------------------------+-------------------------------------------------------------+
| NAME | COMMENT |
+-----------------------------------------+-------------------------------------------------------------+
| buffer_page_read_index_ibuf_leaf | Number of Insert Buffer Index Leaf Pages read |
| buffer_page_read_index_ibuf_non_leaf | Number of Insert Buffer Index Non-Leaf Pages read |
| buffer_page_read_ibuf_free_list | Number of Insert Buffer Free List Pages read |
| buffer_page_read_ibuf_bitmap | Number of Insert Buffer Bitmap Pages read |
| buffer_page_written_index_ibuf_leaf | Number of Insert Buffer Index Leaf Pages written |
| buffer_page_written_index_ibuf_non_leaf | Number of Insert Buffer Index Non-Leaf Pages written |
| buffer_page_written_ibuf_free_list | Number of Insert Buffer Free List Pages written |
| buffer_page_written_ibuf_bitmap | Number of Insert Buffer Bitmap Pages written |
| ibuf_merges_insert | Number of inserted records merged by change buffering |
| ibuf_merges_delete_mark | Number of deleted records merged by change buffering |
| ibuf_merges_delete | Number of purge records merged by change buffering |
| ibuf_merges_discard_insert | Number of insert merged operations discarded |
| ibuf_merges_discard_delete_mark | Number of deleted merged operations discarded |
| ibuf_merges_discard_delete | Number of purge merged operations discarded |
| ibuf_merges | Number of change buffer merges |
| ibuf_size | Change buffer size in pages |
| innodb_ibuf_merge_usec | Time (in microseconds) spent to process change buffer merge |
+-----------------------------------------+-------------------------------------------------------------+
3. Information Schema INNODB_BUFFER_PAGE 表提供有关缓冲池中每个页面的元数据,包括更改缓冲区索引和更改缓冲区位图页面。 PAGE_TYPE 是更改缓冲区页面标识。 IBUF_INDEX 是更改缓冲区索引页面的页面类型,IBUF_BITMAP是更改缓冲区位图页面的页面类型,但是 INNODB_BUFFER_PAGE 表会带来显着的性能开销。为避免影响性能,请在测试实例上重现要调查的问题并在测试实例上运行查询。
例如,我们可以查询 INNODB_BUFFER_PAGE 表以确定页数 IBUF_INDEX 和 IBUF_BITMAP 页数占总缓冲池页数的百分比。
SELECT (SELECT COUNT(*) FROM INFORMATION_SCHEMA.INNODB_BUFFER_PAGE WHERE PAGE_TYPE LIKE 'IBUF%') AS change_buffer_pages, (SELECT COUNT(*) FROM INFORMATION_SCHEMA.INNODB_BUFFER_PAGE) AS total_pages, (SELECT ((change_buffer_pages/total_pages)*100)) AS change_buffer_page_percentage;
+---------------------+-------------+-------------------------------+
| change_buffer_pages | total_pages | change_buffer_page_percentage |
+---------------------+-------------+-------------------------------+
| 2 | 40957 | 0.0049 |
+---------------------+-------------+-------------------------------+
4. Performance Schema 为高级性能监控提供更改缓冲区互斥等待检测。 要查看更改缓冲区检测,操作以下查询:
mysql> SELECT * FROM performance_schema.setup_instruments WHERE NAME LIKE '%wait/synch/mutex/innodb/ibuf%';
+-------------------------------------------------------+---------+-------+
| NAME | ENABLED | TIMED |
+-------------------------------------------------------+---------+-------+
| wait/synch/mutex/innodb/ibuf_bitmap_mutex | YES | YES |
| wait/synch/mutex/innodb/ibuf_mutex | YES | YES |
| wait/synch/mutex/innodb/ibuf_pessimistic_insert_mutex | YES | YES |
+-------------------------------------------------------+---------+-------+
局限性
更改缓冲区占用了缓冲池的一部分,减少了可用于缓存数据页的内存。如果工作集几乎适合缓冲池,或者如果您的表的二级索引相对较少,则禁用更改缓冲可能会有用。如果工作数据集完全适合缓冲池,则更改缓冲不会造成额外开销,因为它仅适用于不在缓冲池中的页面。
知识总结
change buffer 适合写多读少的业务场景(账单、日志类系统),因为即使更新语句使用了 change buffer 机制,但是如果该业务查询场景很多,亦或者需要更新完立刻查询的话。这样会马上触发 merge 操作,这样随机访问IO的次数不仅不会减少,反而增加了 change buffer 的维护成本。
参考文档
https://dev.mysql.com/doc/refman/5.7/en/innodb-change-buffer.html