参考文章:
- 基于Redo Log和Undo Log的MySQL崩溃恢复流程
- MySQL的Double Write并不难理解
- 答疑文章(一):日志和索引相关问题
- 《MySQL技术内幕:InnoDB存储引擎》
作用
double write(两次写)使数据页更可靠。
当InnoDB存储引擎正在向磁盘写入数据页时(16KB的数据页只写入了前4KB),这时发生宕机,这种情况称为部分写失效(partial page write)。如果没有double write(两次写)机制,那么会造成数据丢失的情况。
double write(两次写)正是用来解决上述问题而设计的。
说明:数据页默认为16KB,文件系统一页默认大小为4KB。
原理

如上图所示,double write(两次写)由两部分组成,一部分是内存中的double write buffer ,大小2MB;另一部分是物理磁盘上共享空间中连续的128个页,即2个区,大小2MB。在对缓冲池的脏页刷新时,经过如下步骤:
- 先通过memcpy函数将脏页先复制到double write buffer中
- 然后每次以1MB大小顺序写入共享表空间的物理磁盘上(因为该空间维护的是128个连续页,最小16KB,所以不会出现部分写失效情况)
- 马上调用fsync函数同步磁盘
- 如果在同步磁盘的过程中发生崩溃,InnoDB存储引擎从共享表空间中doublewrite中找到该页的一个副本,将其复制到表空间文件
- 然后再应用重做日志,更新数据页。
PS:总结到这里我有个疑问,在同步磁盘时宕机的问题是解决了,那么如果写入double write共享表空间时宕机,那么数据是不是就丢失了呢?
经过不断的查看资料、思考终于想通了,答案当然是不会丢失。有这个疑问说明对InnoDB存储引擎的整体运行机制的细节不了解导致,具体详解往下看。

- 第一步,对数据页数据进行更新
- 第二步,向redo log buffer中记录redo log
- 第三步,将缓存中的redo log写入磁盘
- 第四步,将脏页复制到double write buffer中
- 第五步,将数据页写入到共享表空间(维护的是128个连续页,最小16KB)
- 第六步,马上调用fsync函数同步磁盘
- 第七步,当第六步执行一半时发生宕机,执行恢复操作,InnoDB存储引擎从共享表空间中doublewrite中找到该页的一个副本,将其复制到表空间文件。
- 如果在执行第五步时,有些数据页还没写入共享表空间就宕机了,那么此时磁盘中就丢失了该数据页,这时就需要靠redo log来恢复数据了。
- 第八步,重启服务,根据redo log文件向缓存池中加载数据页,以一个数据页LSN=1000为例子,该数据页时更新之前的,在redo log中该数据页LSN=1100。
- 第九步,比较redo log与数据页的LSN大小 redo log lsn > page lsn,需要更新数据页,更新完成后该数据页为脏页。
- 此时重复第四步,将脏页复制到double write buffer中
- 继续重复后面所有步骤。