为了账号安全,请及时绑定邮箱和手机立即绑定

MySQL的Double Write并不难理解

标签:
MySQL

问题引入- 断电了#

今天为大家介绍一个新的名词:double write。

相信你还记得,我之前有写笔记跟大家分享过,在MySQL组织数据的基本单位是存在于磁盘上的数据页。数据页被读取到内存(Buffer Pool)中后被称为缓存页。默认情况下每个数据页的大小是16kb,数据页中存储的就是一行行真实的记录,也叫做数据行。

Copy

mysql> SHOW GLOBAL VARIABLES LIKE 'innodb_page_size'; +------------------+-------+ | Variable_name | Value | +------------------+-------+ | innodb_page_size | 16384 | +------------------+-------+

当有写操作对Buffer Pool中的数据页做了更新,我们把这种被改变过的数据页叫做:脏页。它是需要被刷新会磁盘的。这时问题就来了,对于计算机硬件或者是操作系统来说,每次原子IO操作的吞吐量量是小于16KB的,一般每个扇区的大小是512字节。

Copy

`# 文件系统块大小:一般为4k
~]# getconf PAGESIZE
4096

~]# fdisk -l

磁盘 /dev/sda:53.7 GB, 53687091200 字节,104857600 个扇区
Units = 扇区 of 1 * 512 = 512 bytes
扇区大小(逻辑/物理):512 字节 / 512 字节
I/O 大小(最小/最佳):512 字节 / 512 字节
磁盘标签类型:dos
磁盘标识符:0x000a88ef

设备 Boot Start End Blocks Id System
/dev/sda1 * 2048 2099199 1048576 83 Linux
/dev/sda2 2099200 104857599 51379200 8e Linux LVM

磁盘 /dev/mapper/centos-root:50.5 GB, 50457477120 字节,98549760 个扇区
Units = 扇区 of 1 * 512 = 512 bytes
扇区大小(逻辑/物理):512 字节 / 512 字节
I/O 大小(最小/最佳):512 字节 / 512 字节

磁盘 /dev/mapper/centos-swap:2147 MB, 2147483648 字节,4194304 个扇区
Units = 扇区 of 1 * 512 = 512 bytes
扇区大小(逻辑/物理):512 字节 / 512 字节
I/O 大小(最小/最佳):512 字节 / 512 字节`

那这么看来,当你想把一个16K大小的数据页写入到磁盘中时,结果刚写了4k,突然断电机器宕机了。那此时只有一部分是写入成功的。这就是大家常说的: partial page write

不一会 - 来电了#

还是接着上面的描述说,不一会电源正常了,开机重启MYSQL,这时MySQL会进入到崩溃恢复的阶段。

正常的崩溃恢复流程是:

  1. 将数据页从磁盘中读入到内存中
  2. 检查数据页中的LSN标记和redo log中的LSN谁更新,如果相同表示此时的数据页中的数据就是最新的,如果redo log的LSN比数据页中的LSN大,说明数据页中的数据是过时的数据,按redo重做出一份最新数据

但是现在问题是:因为MySQL的Crash是由断电引发的,操作系统都没来得及将数据页完整的写入到磁盘中,导致崩溃恢复的第一步就失败了,因为MySQL会检查出:这个数据页是个不完整的数据页。

想了解更多崩溃恢复的知识,可以看这篇笔记: https://mp.weixin.qq.com/s/6dQnlvjqOo6A0e_h8vST3w

Double write工作流程#

结合double write来看一下一条update sql的执行流程

Step1: 满足update条件的数据页如果不再Buffer Pool中,就进行一次IO操作,将其加载进磁盘。

Step2: 将该数据页修改成脏页。

Step3: 当需要将缓冲池的脏页刷新到 data file 时,并不直接写到数据文件中,而是先拷贝至内存中的 double write buffer。

Step4: 接着从 double write buffer 分两次写入磁盘共享表空间中,每次写入 1MB,并马上调用 fsync 函数,同步到磁盘,避免缓冲带来的问题。

Step5: 完成Step2后,再将两次写缓冲区写入其对应的单独的数据文件。

关于fsync函数可以看我这篇笔记:https://mp.weixin.qq.com/s/tyxd64gGa_SmR6c9vrwf1w

恢复的过程#

图片描述

  1. 将数据页从磁盘中读入到内存中

  2. 检查到数据页损坏了,尝试通过double write恢复数据。

  3. 如果 double write 的数据是完整的,用 double buffer 的数据页替换坏掉的数据页。

那,如果 double write 中的数据页被写坏了怎么办?

其实没关系,因为是先往共享表空间中写double write数据页,再往各个表对应的表空间文件中写实际的数据页,如果double write中的数据页坏点了,那恰恰说明,各个表对应的表空间文件中的数据页没坏!恢复的流程不会被打断!

配置参数#

Copy

`# 查看是否启用了double write,以及相关参数
mysql> SHOW VARIABLES LIKE ‘innodb_doublewrite%’;
±------------------------------±------+
| Variable_name | Value |
±------------------------------±------+
| innodb_doublewrite | ON |
| innodb_doublewrite_batch_size | 120 |
±------------------------------±------+
2 rows in set (0.02 sec)

查询double write的使用情况

mysql> SHOW STATUS LIKE ‘innodb_dblwr_%’;
±---------------------------±------+
| Variable_name | Value |
±---------------------------±------+
| Innodb_dblwr_pages_written | 14615 | #从BP写入到dblwr的page数
| Innodb_dblwr_writes | 636 | #写文件的次数
±---------------------------±------+
2 rows in set (0.02 sec)`

疑问#

看到这里你肯定已经知道了double write解决了数据页被写坏的情况,也就是说,redo log不能对一个本身就坏掉的数据页进行重做。

但是,不知道你有没有这样的疑问,那redo log也是以文件的形式存在于磁盘上的,那假如在write redo log时,断电了呢?那redo log不也被损坏了?那还崩溃恢复个锤子?

答:是这样的:事务产生的redo log先被组织成redo log block。并且redo log block其实就在redo log buffer 中。而redo log block的大小==操作系统一次原子IO的吞吐量512字节就像下图这样:

图片描述

作者:赐我白日梦
出处:https://www.cnblogs.com/ZhuChangwu/p/14725040.html

点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消