参考原文: http://blog.itpub.net/22664653/viewspace-1140915/ id="tmp_downloadhelper_iframe" >

partial page write 问题

InnoDB 的 Page Size 一般是 16KB,其数据校验也是针对这 16KB 来计算的,将数据写入到磁盘是以 Page 为单位进行操作的。而计算机硬件和操作系统,在极端情况下(比如断电)往往并不能保证这一操作的原子性,16K 的数据,写入 4K 时(os 层面的原子 block 一般为 4K,可以调),发生了系统断电 / os crash ,只有一部分写是成功的,这种情况下就是 partial page write 问题。

很多 DBA 会想到系统恢复后,MySQL 可以根据 redolog 进行恢复,而 mysql 在恢复的过程中会检查 page 的 checksum,checksum 就是 page 的最后事务号,发生 partial page write 问题时,page 已经损坏,找不到该 page 中的事务号,就无法恢复。

为了解决 partial page write 问题 ,当 mysql 将脏数据 flush 到 data file 的时候, 先使用 memcopy 将脏数据复制到内存中的 doublewrite buffer ,之后通过 doublewrite buffer 再分 2 次,每次写入 1MB 到共享表空间,然后马上调用 fsync 函数,同步到磁盘上,避免缓冲带来的问题,在这个过程中,doublewrite 是顺序写,开销并不大,在完成 doublewrite 写入后,再将 doublewrite buffer 写入各表空间文件,这时是离散写入。(个人观点:doublewrite 是在刷脏的时候先写的,fsync 完 doublewrite 后再 fsync data file。 如果操作都没问题,doublewrite 可以被后面的数据覆盖,因此不存在 doublewrite 空间不够的情况。)

如果发生了极端情况(断电),InnoDB 再次启动后,发现了一个 Page 数据已经损坏,那么此时就可以从 doublewrite 中进行数据恢复了。(盗图,图可能有问题,个人理解第 5 步应该为: flush redo logfile 到磁盘; 第 6 步应该为: copy dirty page 到 DWB, 再 write 到 tablespace,再 flush; 第 7 步应该为: flush data page 到磁盘; 第 8 步应该为: 从 tablespace 中恢复坏块)

doublewrite 的缺点是什么

位于共享表空间上的 doublewrite 实际上也是一个文件,写 DW 会导致系统有更多的 fsync 操作,即更多的 IO 操作,所以它会降低 mysql 的整体性能。但是并不会降低到原来的 50%。这主要是因为:

  • doublewrite 是一个连续的存储空间,所以硬盘在写数据的时候是顺序写,而不是随机写,这样性能更高。
  • 将数据从 double write buffer 写到真正的 segment 中的时候,系统会合并连接空间刷新的方式,每次可以刷新多个 page(个人理解:这个地方的意思是,合并多个 page 后一次性刷入 doublewrite)。

doublewrite 在恢复的时候是如何工作的

  • If there’s a partial page write to the doublewrite buffer itself, the original page will still be on disk in its real location.
    如果是写 doublewrite 本身失败,那么这些数据不会被写到磁盘,InnoDB 此时会从磁盘载入原始的数据,然后通过 InnoDB 的事务日志来计算出正确的数据,重新写入到 double write buffer.
  • When InnoDB recovers, it will use the original page instead of the corrupted copy in the doublewrite buffer. However, if the doublewrite buffer succeeds and the write to the page’s real location fails, InnoDB will use the copy in the doublewrite buffer during recovery.
    如果 doublewrite 写成功,但是写磁盘失败,InnoDB就不用通过事务日志来计算了,而是直接用 DW 的数据再写一遍.
  • InnoDB knows when a page is corrupt because each page has a checksum at the end; the checksum is the last thing to be written, so if the page’s contents don’t match the checksum, the page is corrupt. Upon recovery, therefore, InnoDB just reads each page in the doublewrite buffer and verifies the checksums. If a page’s checksum is incorrect, it reads the page from its original location.
    在恢复的时候,InnoDB 直接比较页面的 checksum,如果不对的话,就从硬盘载入原始数据,再由事务日志开始推演出正确的数据,所以 InnoDB 的恢复通常需要较长的时间.(补充: 能直接从事务日志和原数据推导出来,那 DW 还有什么用,是在 page 写坏连 page no 都找不到这才是问题吧?)

我们是否一定需要 doublewrite

In some cases, the doublewrite buffer really isn’t necessary—for example, you might want to disable it on slaves. Also, some filesystems (such as ZFS) do the same thing themselves, so it is redundant for InnoDB to do it. You can disable the doublewrite buffer by setting InnoDB_doublewrite to 0.

如何使用 double write

InnoDB_doublewrite = 1表示启动 double write
show status like ‘InnoDB_dblwr%’ 可以查询 double write 的使用情况。

InnoDB_dblwr_pages_written:从bp flush 到 DWB的个数
InnoDB_dblwr_writes :写文件的次数
每次写操作合并 page 的个数 = InnoDB_dblwr_pages_written / InnoDB_dblwr_writes