MySQL - Double Write

Double Write 介绍

为了保证数据写入的可靠性,InnoDB 引入了 Double Write 特性。

具体来说,在刷脏页时先将脏页写入 double write 段中进行备份,写入成功后再将脏页写入数据文件中,保证页面的完整性。

partial write

InnoDB的数据页默认是 16K ,而文件系统的数据页是 4K,磁盘IO操作是按页为单位进行读写的。这就可能出现数据库对一个 16K 的数据页操作后,触发刷盘动作,在该过程中可能数据库宕机导致没有完全将 16K 数据页写到磁盘,这种页就不是一个完整的页,这种现象称为 partial write

要知道,redo log 恢复数据的前提是页是完整的,数据页不完整是没办法使用 redo log 恢复的。那如果发生 partial write 该怎么办?为了解决这个问题, InnoDB 引入了 Double Write 特性。

double write 原理

当 InnoDB 中有数据要变动,会先将其在 Buffer Poll 中的 page 进行更新,并且会记录对应的变动信息到 redo log 中,此时 Buffer Pool 中的该 page 就会被标记为 dirty page(脏页)。在适当的时候,如 Buffer Pool不够、dirty page 在 Buffer Pool 中的比例达到阈值设定等,这些 dirty page 会被刷新到磁盘。

在 dirty page 刷盘过程中,如果突然断电或系统奔溃,16K 的 dirty page 可能只有 4K 或 8K 被写到磁盘上。此时,磁盘上的数据页就被破坏了,是不完整的页,不可使用。

Double Write 机制就是为了解决这个问题的。当将脏数据刷新到磁盘时,会先将脏数据复制到内存中的 double write buffer,之后将该内存数据分 2 次,每次写入 1M 到共享表空间进行持久化备份,这个过程是顺序写的,因此非常快。写入成功后,再将 dirty page 从 Buffer Pool 刷新到数据文件。

Double Write 恢复流程

dirty page 刷盘过程中,如果发生了系统宕机或断电,16K 的数据只有 4K 或者 8K 被写到磁盘上,InnoDB 崩溃恢复时发现 page 损坏(比较页面的checksum),直接从共享表空间的 double write 中找到该页的一个最近的副本,复制到数据文件,再应用 redo log 就完成了恢复过程。因为有副本所以也不担心表空间中数据页是否损坏。

如果在将 dirty page 从内存的 double write buffer 写到共享表空间过程中发生了宕机情况,此时数据文件中的 page 还是干净的,奔溃恢复时 InnoDB 可以利用 redo log 来进行数据的恢复。

Double Write 流程

从 Double Write 流程图中可以看出,在奔溃恢复的时候,还使用到了 redo log 。这是因为有些 dirty page 的改动并没有写入到共享表空间的 double write 中,这部分数据需要使用 redo log 来恢复。

附加

一般来说,开启 Double Write 的性能会降低5%~25%的样子,但还是建议使用 Double Write 来保证数据写入的可靠性。

  • 是否开启 Double Write

    innodb_doublewrite 参数控制是否打开 Double Write,默认开启状态。

  • Double Write 使用情况

上图显示,从 Buffer Pool 一共将 22842199 个 page 复制到 double write buffer 中,一共刷盘 1959018 次到系统表空间。相当于每次刷盘了 22842199/1959018 ~= 11.67 个数据页。从这里也能看出,开启 Double Write 不会对效率产生很大影响。