MySQL采用buffer机制,避免每次读写都要磁盘IO,提高效率:《??缓冲池(buffer pool??)》《??写缓冲(change buffer)??》《??日志缓冲(log buffer)??》MySQL的bufferpagesize为16K,文件系统pagesize为4K。当一页数据刷入磁盘时,必须写入文件系统中的四页。如上图所示,MySQL中page=1的页面在物理上对应于磁盘上的1+2+3+4这四个格子。那么,问题来了,这个操作不是原子的,如果执行到一半就断电了,会不会有什么问题呢?是的,这称为“页面数据损坏”。如上图所示,MySQL中page=1的页面准备刷入磁盘,而文件系统中只有3个页面被刷入。断电后会出现:重启后,page=1的页面物理对应磁盘1+2+3+4的四个格子,数据完整性被破坏。画外音:redo无法修复这种“pagedatacorruption”异常,修复的前提是“pagedata正确”且redolog正常。如何解决这种“页面数据损坏”问题?简单的想法就是有一个“副本”来恢复原来的页面。存放“副本”的地方就是DoubleWriteBuffer。DoubleWriteBuffer,但又不同于传统的缓冲区,它分为内存和磁盘两层架构。画外音:大部分传统的buffer都保存在内存中;而DWB中的数据需要落地。如上图,当有页面数据需要刷入时:第一步:先memcopy页面数据到DWB内存中;第二步:先将DWB内存中的页面数据刷写到DWB盘中;step3:在DWB的内存中,刷写到数据盘存储;画外音:DWB共有128页,容量只有2M。第2步和第3步需要两次写入磁盘,这就是“DoubleWrite”的由来。为什么DWB可以解决“页面数据损坏”的问题?假设第2步断电,1+2+3+4的完整数据还在磁盘中。画外音:只要页面数据完整,就可以通过redo恢复数据。如果在步骤3中断电,则完整数据将存储在DWB中。因此,一定不会出现“页面数据损坏”的问题。画外音:写了两遍,一处数据一直OK。试了几十次,还是无法重现“页面数据损坏”。在网上找了一张图,当“页面数据损坏”时,MySQL重启过程使用DWB修复页面数据。可以看到在启动过程中:InnoDB检测到上次异常关闭;尝试恢复ibd数据,但失败;从DWB恢复写了一半的页面;通过DWB可以保证page数据的完整性,但是DWB毕竟需要写两个从盘会不会导致数据库性能急剧下降?分析DWB执行的三个步骤:第一步,pagedatamemcopy到DWB内存,速度很快;第二步,DWBmemoryfsyncflashtoDWBdisk,属于sequentialappendwriting,速度也很快;第三步step,刷盘,乱写,必填,不做额外操作;另外,128页(每页16K)2M的DWB会刷入磁盘两次,每次最多64页,即1M数据。执行速度也非常快。综上所述,性能会受到影响,但影响不大。画外音:write-ahead-log之所以性能高,是因为它是顺序写入;有第三方测评,估计性能损失在10%左右。更具体地说,InnoDB中有两个变量可以用来检查doublewritebuffer:Innodb_dblwr_pages_written:记录DWB中写入的页数。Innodb_dblwr_writes:记录DWB写操作的次数。可以通过以下方式查询:showglobalstatuslike"%dblwr%"。最后,MySQL有强大的数据安全机制:异常crash时,如果没有发生“页面数据损坏”,可以通过redo恢复数据;当发生“页数据损坏”时,可以通过双写缓冲区恢复页数据;doublewritebuffer:不是内存缓冲区,而是内存/磁盘两层结构,是InnoDB中On-Disk架构的重要组成部分;它是一种通过两次写入来确保页面完整性的机制;知道它,知道它为什么。思路比结论更重要,希望大家有所收获。
