一、基本概念

变更缓冲是为了缓存缓冲池(Buffer Pool)中不存在的二级索引(Secondary Index)页的变更操作的一种特殊的内存数据结构。
这些变更通常是一些Insert、Update、Delete等DML操作引发的,如果有一些其它的读操作将这些被变更的二级索引页加进了缓冲池(Buffer Pool),则这些变更会被马上合并至缓冲池中以保证用户可以读取到一致的数据。

变更缓冲架构图如下:
深入理解变更缓冲-Change Buffer - 图1

与聚集索引不同,辅助索引通常不是唯一的,插入到辅助索引中的顺序相对随机。类似地,删除和更新可能会影响不在索引树中邻接的二级索引页。当其他操作将受影响的页读入缓冲池时,在稍后合并缓存的更改,可以避免将辅助索引页从磁盘读入缓冲池所需的大量随机访问I/O。

当系统大部分时间处于空闲状态时或在缓慢关闭期间运行的清除操作将更新后的索引页写入磁盘。与将每个值立即写入磁盘相比,清除操作可以更有效地为一系列索引值写入磁盘块。

当有许多受影响的行和大量二级索引需要更新时,变更缓冲区合并可能需要几个小时。在此期间,磁盘I/O会增加,这可能导致磁盘绑定查询的速度显著减慢。变更缓冲区合并也可能在事务提交后继续发生,甚至在服务器关闭和重新启动之后。

在内存中,变更缓冲区占用了缓冲池(Buffer Pool)的一部分。在磁盘上,变更缓冲区是系统表空间的一部分,当数据库服务器关闭时,将在其中缓冲索引更改。

如果二级索引包含降序索引列,或者主键包含降序索引列,则不支持对该二级索引进行变更缓冲。

二、配置变更缓冲

在表上执行插入、更新和删除操作时,索引列的值(特别是辅助键的值)通常是无序的,需要大量的I/O来使辅助索引保持最新。当相关页不在缓冲池中时,变更缓冲区将变更缓存到辅助索引项,因此不必立即从磁盘读入页,从而避免了昂贵的I/O操作。当页面加载到缓冲池中时,缓冲的更改将被合并,更新后的页面稍后将刷新到磁盘。InnoDB的主线程在服务器接近空闲时和慢速关机时合并缓冲变化。

因为它可以减少磁盘读写,所以变更缓冲区特性对于I/ o限制的工作负载最有价值,例如具有大量DML操作(如批量插入)的应用程序。

但是,变更缓冲区占用了缓冲池的一部分,减少了用于缓存数据页的可用内存。如果工作集几乎可以装入缓冲池,或者如果表的二级索引相对较少,那么禁用变更缓冲可能会很有用。如果工作数据集完全适合缓冲池,则变更缓冲不会带来额外的开销,因为它只适用于不在缓冲池中的页面。

您可以使用innodb_change_buffering配置参数来控制InnoDB执行变更缓冲的程度。您可以启用或禁用insert、delete操作(当索引记录最初被标记为删除时)和purge操作(当索引记录被物理删除时)的缓冲。更新操作是插入操作和删除操作的组合。默认的innodb_change_buffering值是all。

允许innodb_change_buffering值包括:
all - 默认值:缓冲区插入、删除标记操作和清除
none - 不要缓存任何操作
inserts - 缓存插入操作
deletes - 缓存删除标记(delete-marking)操作
changes - 缓存插入和删除标记(delete-marking)操作
purges - 缓冲在后台发生的物理删除操作

您可以在MySQL选项文件(my.cnf或my.ini)中设置innodb_change_buffering参数,也可以使用SET GLOBAL语句动态更改它,这需要足够的权限来设置全局系统变量。改变设置会影响新操作的缓冲; 现有缓冲项的合并不受影响。

三、配置变更缓冲区大小

innodb_change_buffer_max_size变量允许将变更缓冲区的最大大小配置为缓冲池总大小的百分比。默认情况下,innodb_change_buffer_max_size设置为25。最大设置为50。

考虑在一个有大量插入、更新和删除活动的MySQL服务器上增加innodb_change_buffer_max_size,其中变更缓冲区合并跟不上新的变更缓冲区条目,导致变更缓冲区达到其最大大小限制。

考虑在MySQL服务器上使用用于报告的静态数据来减少innodb_change_buffer_max_size,或者如果变更缓冲区消耗了太多与缓冲池共享的内存空间,导致页面比预期更快地从缓冲池中老化。

使用具有代表性的工作负载测试不同的设置,以确定最佳配置。innodb_change_buffer_max_size设置是动态的,允许在不重启服务器的情况下修改设置。

四、监控变更缓冲

以下选项可用于变更缓冲区监控:

(1)、InnoDB标准监控输出包括改变缓冲区状态信息。要查看监视器数据,发出显示引擎INNODB状态语句。

mysql> SHOW ENGINE INNODB STATUS\G

变更缓冲区状态信息位于插入缓冲区和自适应哈希索引标题下,类似于以下内容:
——————————————————-
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

(2)、INFORMATION_SCHEMA.INNODB_METRICS表提供了InnoDB标准监视器输出中的大部分数据点,以及其他数据点。要查看变更缓冲区指标和每个指标的描述,发出以下查询:

mysql> SELECT NAME, COMMENT FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE NAME LIKE ‘%ibuf%’\G

(3)、INFORMATION_SCHEMA.INNODB_BUFFER_PAGE表提供了关于缓冲池中每个页面的元数据,包括变更缓冲区索引和变更缓冲区位图页面。变更缓冲区页面由PAGE_TYPE标识。IBUF_INDEX是用于变更缓冲区索引页的页面类型,IBUF_BITMAP是用于变更缓冲区位图页的页面类型。

警告:
查询INNODB_BUFFER_PAGE表可能会带来显著的性能开销。为了避免影响性能,请在测试实例上重现您想要调查的问题,并在测试实例上运行您的查询。

例如,可以查询INNODB_BUFFER_PAGE表,以确定IBUF_INDEX和IBUF_BITMAP页面的大致数量占缓冲池页面总数的百分比。

mysql> 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 |
+———————————-+——————-+—————————————————-+
| 25 | 8192 | 0.3052 |
+———————————-+——————-+—————————————————-+

(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 |
+————————————————————————————————+—————-+———-+