暂无图片
暂无图片
3
暂无图片
暂无图片
暂无图片

MySQL各种“Buffer”之Doublewrite Buffer

GrowthDBA 2021-09-27
4558
上一篇文章MySQL各种“Buffer”之Adaptive Hash Index我们学习了InnoDB Adaptive Hash Index自适应哈希索引的工作原理。其本质是将频繁访问数据页的索引键值以“Key”放在缓存中,“Value”为该索引键值匹配完整记录所在页面(Page)的位置,通过缩短寻路路径(Search Path)从而提升MySQL查询性能的一种方式。

而今天要讲解的Doublewrite Buffer被MySQL放在官档目录“磁盘结构”中了,但真实情况是Doublewrite Buffer是内存+磁盘的结构

今天就学习了解一下Doublewrite Buffer是什么?工作原理是怎样的?(PS:英文名:Doublewrite Buffer,翻译成中文:双写缓冲区,缩写:DWB

参考资料:58沈剑-架构师之路-公众号文章《double write buffer,你居然没听过?》

预备知识必知

不同程序的Page大小

操作系统可以看成是一个程序,作为程序而言,都有最小处理单位的说法,我们常见的服务器一般都是Linux操作系统,对应文件系统的页(OS Page)就可以看成是Linux操作系统与文件系统交互的最小单位。可以通过下面的命令来查看:

getconf PAGESIZE

通过结果可以看出,Linux文件系统页(OS Page)的大小是4KB。同样地,MySQL作为一个程序也是有最小交互单位的,即InnoDB Buffer Pool能处理的最小单位,可以通过以下SQL命令查看:

SHOW VARIABLES LIKE 'innodb_page_size';

如图所示,MySQL的页(Page)大小默认是16KB

不同大小Page的程序在交互过程中会出现什么问题

一般情况下,除了操作系统的页(OS Page)为4KB之外,其余程序的页(Page)都会大于等于操作系统的页大小,比如,Oracle的Page大小为8KB。MySQL的Page大小也可以通过上面innodb_page_size参数指定(<5.6版本时,不可调整;5.6版本时,可自定义为8KB、4KB,但不能调大【调小是为了调高数据对齐概率】;5.7以上,才可以改成32KB、64KB【且最好设置成默认值16KB的整数倍,这是全局选项,无法在MySQL运行过程中动态修改】。说了这么多,其实不用修改,使用默认值即可)

有点扯远了。我们知道操作系统的页大小和MySQL的页大小了,而且MySQL程序是跑在Linux操作系统上的,所以可以得出一个结论:MySQL将Buffer Pool中一页数据刷入磁盘,要写4个文件系统里的页(也可以说成一个MySQL数据页映射4个系统页)

如上图所示,MySQL里Page 1的页,物理上对应磁盘的Page 1、Page 2、Page 3、Page 4四个页。
这个操作并非原子,如果执行到一半断电,会不会出现问题呢?
答案:会,这就是所谓的“页数据损坏

如上图所示,MySQL内Page 1的页准备刷入磁盘,才刷了3个文件系统里的页,掉电了,则会出现:重启后,MySQL内Page 1的页,物理上对应磁盘上的Page 1、Page 2、Page 3三个页,数据完整性被破坏。(Redo Log无法修复这类“页数据损坏”的异常,修复的前提是“页数据正确”并且Redo日志正常

Doublewrite Buffer工作原理

Doublewrite Buffer概念

针对上面出现的情况,如何解决这类“页数据损坏”的问题呢?很容易想到的方法是,能有一个“副本”,对原来的页进行还原,这个存储“副本”的地方,就是Doublewrite BufferDoublewrite Buffer,它与传统的“Buffer”又不同,它分为内存和磁盘的两层架构。(传统的“Buffer”,大部分是内存存储;而DWB里的数据,是需要落地的)

Doublewrite Buffer工作流程

(Doublewrite Buffer工作流程图)

如上图所示,当有页数据要刷盘时:

第1步:页数据先memcopy到DWB的内存里;

第2步:DWB的内存里的数据页,会先刷到DWB的磁盘上;

第3步:DWB的内存里的数据页,再刷到数据磁盘存储.ibd文件上;

备注:DWB内存结构由128个页(Page)构成,所以容量只有:16KB × 128 = 2MB。

Doublewrite Buffer Q&A环节

Q-1:DWB为什么能解决“页数据损坏”问题呢?

A-1:假设步骤2掉电,磁盘里依然是Page 1、Page 2、Page 3、Page 4的完整数据。只要有页数据完整,就能通过Redo Log还原数据;假如步骤3掉电,DWB磁盘结构里存储着完整的数据。所以,一定不会出现“页数据损坏”问题。同时写了DWB磁盘和Data File,总有一个地方的数据是OK的。

Q-2:为什么叫Doublewrite?

A-2:步骤2和步骤3要写2次磁盘,这就是“Doublewrite”的由来。

Q-3:能够通过DWB保证页数据的完整性,但毕竟DWB要写两次磁盘,会不会导致数据库性能急剧降低呢?

A-3:分析DWB执行的三个步骤:

第1步:页数据memcopy到DWB的内存,速度很快;

第2步:DWB的内存fsync刷到DWB的磁盘,属于顺序追加写,速度也很快;

第3步,刷磁盘,随机写,本来就需要进行,不属于额外操作;

另外,128页(每页16K)2M的DWB,会分两次刷入磁盘,每次最多64页,即1M的数据(所以图中我们的DWB磁盘结构分为了两部分),执行也是非常之快的。综上,性能会有所影响,但影响并不大。

Doublewrite Buffer相关参数和变量

SHOW VARIABLES LIKE 'innodb_doublewrite';SHOW GLOBAL STATUS LIKE '%dblwr%';

  • 参数:innodb_doublewrite

介绍:Doublewrite Buffer是否启用开关,默认是开启状态,InnoDB将所有数据存储两次,首先到双写缓冲区,然后到实际数据文件。

  • 变量:Innodb_dblwr_pages_written

介绍:记录写入到DWB中的页数量。

  • 变量:Innodb_dblwr_writes

介绍:记录DWB写操作的次数。

知识补充

MySQL不同版本间Doublewrite Buffer的差异

上面讲解的内容是基于MySQL 5.7。MySQL 5.6和MySQL 5.7的Doublewrite Buffer结构和参数类似,看下官档中给出的InnoDB体系结构图。

  • MySQL 5.6

(https://dev.mysql.com/doc/refman/5.6/en/innodb-architecture.html)
  • MySQL 5.7

(https://dev.mysql.com/doc/refman/5.7/en/innodb-architecture.html)

从上面的图片对比,可以看出,MySQL 5.6MySQL 5.7的InnoDB体系结构变化还是很大的。但是Doublewrite Buffer的结构没有发生变化,DWB磁盘结构的数据都存放在共享系统表空间(System Tablespace-ibdata1)中

MySQL 8.0中Doublewrite Buffer的变化

在MySQL 8.0中,Doublewrite Buffer发生了一些变化,DWB磁盘结构的数据存放从共享系统表空间中分离出来,来看一下MySQL 8.0 InnoDB体系结构图。
  • MySQL 8.0

(https://dev.mysql.com/doc/refman/8.0/en/innodb-architecture.html)

从图中我们可以看出,MySQL 8.0和MySQL 5.6、5.7不同的地方是,在磁盘结构中,从原来的System Tablespace变成了Doublewrite Buffer Files了:

同时你会发现涉及Doublewrite Buffer的参数也多了:

SHOW VARIABLES LIKE '%double%';

和MySQL 5.6、5.7版本相同的参数我们不再赘述,来看下MySQL 8.0新增的参数:(https://dev.mysql.com/doc/refman/8.0/en/innodb-doublewrite-buffer.html
  • 参数:innodb_doublewrite_batch_size

介绍:innodb_doublewrite_batch_size变量(在MySQL 8.0.20中引入)控制要批量写入的双写页数。此变量用于高级性能调整。默认值应该适合大多数用户(默认值是0,最大值是256)。
  • 参数:innodb_doublewrite_dir

含义:innodb_doublewrite_dir变量(在MySQL 8.0.20中引入)定义了目录InnoDB创建双写文件。如果未指定innodb_data_home_dir目录,则在目录中创建双写文件,如果未指定,则默认为数据目录。(我们上面的截图中就没有指定,所以双写文件创建在了数据目录)。
哈希符号”#“会自动添加到指定目录名称的前缀,以避免与模式名称冲突。但是,如果是'.'、'#'。或“/”前缀在目录名称中明确指定,哈希符号“#”不作为目录名称的前缀。(理想情况下,doublewrite目录应放置在可用的最快存储介质上
  • 参数:innodb_doublewrite_files

含义:innodb_doublewrite_files变量定义了双写文件的数量。默认情况下,为每个缓冲池实例创建两个双写文件:一个刷新列表双写文件和一个LRU列表双写文件。刷新列表双写文件用于从缓冲池刷新列表刷新的页面。刷新列表双写文件的默认大小InnoDB页面大小 * 双写页面字节。LRU列表双写文件用于从缓冲池LRU列表中刷新的页面。它还包含用于单页刷新的插槽。LRU列表双写文件的默认大小InnoDB页面大小 *(双写页面 +(512 缓冲池实例数)),其中512是为单页刷新保留的插槽总数至少有两个双写文件。双写文件的最大数量是缓冲池实例数量的两倍。(缓冲池实例的数量由innodb_buffer_pool_instances变量控制)
Doublewrite文件名具有以下格式:例如,以下双写文件是为页面大小为16KB和单个缓冲池的MySQL实例创建的:#ib_page_size_file_number.dblwr
所以我们的DWB文件就被创建成下面的模样(没毛病吧!?老铁):
#ib_16384_0.dblwr#ib_16384_1.dblwr

innodb_doublewrite_files变量用于高级性能调整。默认设置应该适合大多数用户。
  • 参数:innodb_doublewrite_pages

含义:innodb_doublewrite_pages变量(在MySQL 8.0.20引入)控制每个线程双写页的最大数目。如果未指定值,innodb_doublewrite_pages则设置为该innodb_write_io_threads值。此变量用于高级性能调整。默认值应该适合大多数用户。(最小值和默认值一样,都是innodb_write_io_threads的值,最大值是256)

小结

今天理论的知识不是很多,下面简单做一下总结:

1、1个MySQL的页(Page)可以映射4个操作系统(文件系统)的页(OS Page),Doublewrite Buffer是为了保证因系统页损坏导致的MySQL数据丢失的保证方案;
2、Doublewrite Buffer是内存+磁盘结构,内存结构是由128个页组成,大小为16KB × 128 = 2MB,“Double”的由来是因为数据写两次磁盘:一次是写DWB的磁盘结构(此过程分两次写入,每次写入16KB × 64 = 1MB的数据),一次是直接写入Data File(.ibd);
3、MySQL 8.0较MySQL 5.6、5.7版本的Doublewrite Buffer产生了一些新的变化,DWB磁盘结构的数据存放从共享系统表空间中分离出来,存放在单独的.dblwr文件中;
4、MySQL 8.0新增了一些涉及Doublewrite Buffer的参数,可以更细粒度的管理Doublewrite Buffer,但新增参数多用于高级性能调整,我们使用默认值即可,无需更多关注。
今天主要讲解了MySQL InnoDB Doublewrite Buffer的工作原理,通过流程图的方式来说明Doublewrite Buffer的工作流程,偏理论的知识,内容比较少也很好理解,大家理解记忆即可,面试也会遇到这个知识点。



end


文章转载自 GrowthDBA,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论