InnoDB的页和操作系统的页大小不一致,InnoDB页大小一般为16K,操作系统页大小为4K,InnoDB的页写入到磁盘时,一个页需要分4次写。
如果存储引擎正在写入页的数据到磁盘时发生了宕机,可能出现页只写了一部分的情况,比如只写了4K,就宕机了,这种情况叫做部分写失效(partial page write),可能会导致数据丢失。
双写缓冲区 Doublewrite Buffer
为了解决写失效问题,InnoDB实现了double write buffer Files, 它位于系统表空间,是一个存储区域。
在BufferPool的page页刷新到磁盘真正的位置前,会先将数据存在Doublewrite 缓冲区。这样在宕机重启时,如果出现数据页损坏,那么在应用redo log之前,需要通过该页的副本来还原该页,然后再进行redo log重做,double write实现了InnoDB引擎数据页的可靠性.
默认情况下启用双写缓冲区,如果要禁用Doublewrite 缓冲区,可以将 innodb_doublewrite
设置为0。
mysql> show variables like '%innodb_doublewrite%'; +--------------------+-------+ | Variable_name | Value | +--------------------+-------+ | innodb_doublewrite | ON | +--------------------+-------+ 1 row in set (0.01 sec)
数据双写流程
-
step1:当进行缓冲池中的脏页刷新到磁盘的操作时,并不会直接写磁盘,每次脏页刷新必须要先写double write .
-
step2:通过memcpy函数将脏页复制到内存中的double write buffer .
-
step3: double write buffer再分两次、每次1MB, 顺序写入共享表空间的物理磁盘上, 第一次写.
-
step4: 在完成double write页的写入后,再将double wirite buffer中的页写入各个表的独立表空间文件中(数据文件 .ibd), 第二次写。
为什么写两次 ?
可能有的同学会有疑问,为啥写两次,刷一次数据文件保存数据不就可以了,为什么还要写共享表空间 ? 其实是因为共享表空间是在ibdbata文件中划出2M连续的空间,专门给double write刷脏页用的, 由于在这个过程中,double write页的存储是连续的,因此写入磁盘为顺序写,性能很高;完成double write后,再将脏页写入实际的各个表空间文件,这时写入就是离散的了.
doublewrite缓冲区是一个存储区,在该存储区中,InnoDB将页面写入InnoDB数据文件中的适当位置之前,先从缓冲池中刷新页面 。如果在页面写入过程中存在操作系统,存储子系统或意外的mysqld进程退出,则InnoDB可以在崩溃恢复期间从doublewrite缓冲区中找到页面的良好副本。注意:系统恢复后,MySQL 可以根据redolog 进行恢复,而mysql在恢复的过程中是检查page的checksum,checksum就是pgae的最后事务号,发生partial page write 问题时,page已经损坏,找不到该page中的事务号,就无法恢复。
为什么需要双写?个人理解宏观上还是与InnoDB需要支持事务(ACID)特性有关,而底层的原因是为了解决Partial Write Page问题。
InnoDB的页大小默认为16K,可以使用参数innodb_page_size设置, 可设置的值有: 64KB,32KB,16KB(默认),8KB和4KB。并且在数据校验时也针对页进行计算,即他们是一个整个对待,包括把数据持久化到磁盘的操作。而计算机的硬件和操作系统在极端情况下(比如断电、系统崩溃)时,刚写入了4K或8K数据,那么就不能保证该操作的原子性,称为部分页面写问题(Partial Write Page)。
此时就引入了双写缓存区的机制,当发生极端情况时,可以从系统表空间的Double Write Buffer【磁盘上】进行恢复,下面是InnoDB的架构图、双写和恢复流程图。为了方便对比,将组件放在了相同的位置:
这样在极端情况下也能解决 Partial Write page问题了,但是如果我自己的系统本身数据要求没有那么高(比如日志数据库),这样的话毕竟双写是有一定的性能开销的。可以通过参数innodb_doublewrite = 0进行关闭,设置为1表示开启。
官方认为,尽管需要写入两次数据,但是写缓冲区不需要两次的 io开销或操作,因为只需要调用一次操作系统的fsync() 就可以将批量数据顺序写入磁盘 -> 系统表空间的Double Write Buffer(如上图),这里是顺序写而不是随机写(性能可以保证),当然前提是配置刷盘策略参数innodb_flush_method为默认的O_DIRECT。
其实还有一点就是真正提交的时候会使用组提交,我们可以用参数控制:binlog_group_commit_sync_delay:组提交执行fsync() 延迟的微妙数,延迟时间越长批量数据越多,磁盘io越少性能越高。
binlog_group_commit_sync_no_delay_count:组提交执行fsync的批个数。
知识来源:马士兵教育
Mysql - InnoDB三大特性之双写缓冲区(Double Write Buffer)_it_lihongmin的博客-CSDN博客