MySQL双写机制的目的是什么?是如何实现的?
一、双写的目的
为了解决 partial page write 问题。
InnoDB 的Page Size一般是16KB,其数据校验也是针对这16KB来计算的,将数据写入到磁盘是以Page为单位进行操作的。而计算机硬件和操作系统,写文件是以4KB作为单位的,那么每写一个innodb的page到磁盘上,在os级别上需要写4个块.通过以下命令可以查看文件系统的块大小:
- dumpe2fs /dev/vda1 |grep "Block size"
在极端情况下(比如断电)往往并不能保证这一操作的原子性,16K的数据,写入4K 时,发生了系统断电/os crash ,只有一部分写是成功的,这种情况下就是 partial page write 问题。有人会想到系统恢复后MySQL可以根据redolog 进行恢复,而mysql在恢复的过程中是检查page的checksum,checksum就是pgae的最后事务号,发生partial page write 问题时,page已经损坏,找不到该page中的事务号,就无法恢复。
二、double write 原理
为了解决 partial page write 问题 :
- 当mysql将脏数据flush到data file的时候,
- 先使用memcopy 将脏数据复制到内存中的double write buffer ,
- 之后通过double write buffer再分2次,每次写入1MB到共享表空间,
- 然后马上调用fsync函数,同步到磁盘上,避免缓冲带来的问题;
- 在这个过程中,doublewrite是顺序写,开销并不大,
- 在完成doublewrite写入后,再将double write buffer写入各表空间文件,这时是离散写入。
如果发生了极端情况(断电),InnoDB再次启动后,发现了一个Page数据已经损坏,那么此时就可以从doublewrite buffer中进行数据恢复了。
三、两次写的作用
在数据库启动时(异常关闭的情况下),都会做数据库恢复(redo)操作,恢复的过程中,数据库都会检查页面是不是合法(校验等等),如果发现一个页面校验结果不一致,则此时会用到两次写这个功能,这个特点也正是为了处理这样的错误而设计的。
此时的操作很明白了,将两次写的2个BLOCK(簇)都读出来,然后将所有这些页面写回到对应的页面中去,那么这时可以保证这些页面是正确的,并且是在写入前已经更新过的(最新数据)。在写回对应页面中去之后,那么就可以在这基础上继续做数据库恢复了,之后则不会再遇到这样的问题了,因为已经将最后有可能产生写断裂的数据页面都恢复了。
如果是写doublewrite buffer本身失败,那么这些数据不会被写到磁盘,InnoDB此时会从磁盘载入原始的数据,然后通过InnoDB的事务日志来计算出正确的数据,重新写入到doublewrite buffer。