MySQL · 杂谈 · InnoDB Double Write Buffer 那些事儿

我们知道,由于文件系统对一次大数据页(例如InnoDB的16KB)大多数情况下不是原子操作,这意味着如果服务器宕机了,可能只做了部分写入。在InnoDB看来,这样的数据页是无法通过checksum验证的。即时我们强制让其通过验证,也无法从崩溃中恢复,因为当前InnoDB存在的一些日志类型,有些是逻辑操作,并不能做到幂等。

为了解决这个问题,InnoDB实现了double write buffer,简单来说,就是在写数据页之前,先把这个page写到一块独立的物理文件位置,然后再写到数据页。这样在宕机重启的时候,损坏的数据页可以从其中提取出来。

我之前的这篇博客有对dblwr的介绍,这里简单的再介绍一下。

在官方版本中,InnoDB的double write buffer存储在ibdata系统表空间中,大小为2MB,共128个Page,其中120个用于批量写脏,另外8个用于Single Page Flush。做区分的原因是批发刷脏是后台线程做的,不影响前台线程。而Single page flush是用户线程发起的,需要尽快的刷脏并替换出一个空闲页出来。

对于批量刷脏,每次找到一个可做flush的page,对其持有S lock,然后将该page拷贝到dblwr中,当dblwr满后者一次批量刷脏结束时,将dblwr中的page全部刷到ibdata中,注意这是同步写操作;然后再唤醒后台IO线程去写数据页。当后台IO线程完成写操作后,会去更新dblwr中的计数以腾出空间,释放block上的S锁,完成写入。

对于Single Page Flush,则做的是同步写操作,在挑出一个可以刷脏的page后,先加入到dblwr中,刷到ibdata,然后写到用户表空间,完成后,会对该用户表空间做一次fsync操作。

Single Page Flush在buffer pool中free page不够时触发,通常由前台线程发起,由于每次single page flush都会导致一次fsync操作,在大并发负载下,如果大量线程去做flush,很显然会产生严重的性能下降。Percona在5.6版本中做了优化,可以选择由后台线程lru manager来做预刷,避免用户线程陷入其中。

Mariadb/MySQL的改进

dblwr引入了一次额外写的开销,每个数据页都被要求写两次,究其原因是因为写入不是原子的。但如果你的数据表空间放在FusionIO/DirectFS文件系统上,就可以获得原子写特性。

MariaDB使用参数innodb_use_atomic_writes来控制原子写行为,当打开该选项时,会使用O_DIRECT模式打表空间,通过posix_fallocate来扩展文件(而不是写0扩展),当在启动时检查到支持atomic write时,即使开启了innodb_doublewrite,也会关闭掉。

Oracle MySQL同样支持FusionIO的Atomic Write特性(Fusion-io Non-Volatile Memory (NVM) file system),对于支持原子写的文件系统,也会自动关闭double write buffer,具体见该commit(Bug#18069105 - ADD FUSIONIO ATOMIC WRITE SUPPORT FOR LINUX)以及commit2

Facebook改进

实际上这不能算是改进,只是提供了一个新的选项。在现实场景中,宕机是非常低概率的事件。大部分情况下dblwr都是用不上的。但如果我们直接关闭dblwr,如果真的发生例如掉电宕机了,我们需要知道哪些page可能损坏了。

因此Facebook MySQL提供了一个选项,可以写page之前,只将对应的page number写到dblwr中(而不是写全page),在崩溃恢复时,先读出记录在dblwr中的page号,检查对应的数据页是否损坏,如果损坏了,那就需要从备库重新恢复该实例。

Percona 5.7改进

Percona Server的每个版本都对InnoDB的刷脏逻辑做了不少的优化,进入5.7版本也不例外。在官方5.7中已经实现了多个Page Cleaner,我们可以把Page Cleaner配置成和buffer pool instance的个数相同,可以更好的实现并行刷脏。

但是官方版本中,Page cleaner既要负责刷FLUSH LIST,同时也要做LRU FLUSH(但每个bp instance不超过innodb_lru_scan_depth)。而这两部分任务是可以独立进行的。

因此Percona Server增加了多个LRU FLUSH线程,可以更高效的进行lru flush,避免用户线程陷入single page flush状态。每个buffer pool instance拥有自己的lru flush线程和page cleaner线程。lru flush基于当前free list的长度进行自适应计算。 每个lru线程负责自己的那个Buffer pool。因此不同lru flush线程的繁忙程度可能是不一样的。

MULTI-LRU-FLUSH代码见github,以及Percona的博客

在解决上述问题后,bp flush的并行效率大大的提升了。但是对于所有的刷脏操作,都需要走到double write buffer。这意味着dblwr成为了新的瓶颈。为了解决这个问题,dblwr进行了拆分,每个bp instance都有自己的dblwr区域。这样各个Lru flush线程及Page cleaner线程在做page flush时就不会相互间产生锁冲突,从而提升了系统的扩展性。

你可以通过参数来配置一个独立于ibdata之外的文件来存储dblwr,文件被划分成多个区域,分区数为bp instance的个数,每个分区的大小为2 * srv_doublewrite_batch_size,每个batch size默认配置为120个page,其中一个用于刷FLUSH LIST,一个用于刷LRU。

如果fast shutdown设置为2,dblwr文件在正常shutdown时会被删除掉,并在重启后重建。

dblwr-split代码见github: pull#377以及Percona的博客

Aurora

Aurora是一个闭源的MySQL数据库引擎,具体的实现我们不得而知,根据其公开的信息表明,其采用了存储和数据库服务器分离的方式来实现架构。基于这种架构,提供服务的服务器无需去写数据文件,只需要存储日志即可,存储层单独做日志apply到数据页。基于这种实现,也就无需开启double write buffer。

Aurora相关链接:
Youtube视频,需翻墙
AWS Aurora Benchmark - Choose the right tool for the job
Amazon Aurora: Amazon’s New Relational Database Engine
Amazon Aurora: the Benefits and the Drawbacks
Introducing Amazon Aurora: A New MySQL-Compatible Database

时间: 2024-08-31 01:54:49

MySQL · 杂谈 · InnoDB Double Write Buffer 那些事儿的相关文章

[MySQL 5.6] double write buffer的几个关键函数

一个double write buffer 有2MB, 共128个page,在MySQL 5.6中, 默认有120个page用于批量刷新(如 LRU Flush 或者FLUSH LIST FLUSH),剩下的8个Page用于单个page的flush. 在DEBUG版本下,120是可以通过参数innodb_doublewrite_batch_size来配置的,好吧.我已经不安分的把DEBUG宏给去掉了. 全局对象buf_dblwr, 对应结构体为buf_dblwr_t: ib_mutex_t  m

MySQL之Double Write Buffer分析

之前有阅读过相关的文档和资料,总归差了点意思,这次抽出时间仔细推敲了一下,把一些结果记录下来: 杨大师的博文给了很大的帮助:http://blog.itpub.net/22664653/viewspace-1140915/-------------------------------------------------------------------------------------正文----------------------------------------------------

从MySQL中的double write问题说开去

有句话说得好,世上只有两种工具,一种是被人骂的,另一种是没人用的.被骂得越多,侧面反映出关注度越高,使用率越高,越用越成熟,这一点上, MySQL就是一个很不错的例子.而MySQL可支持的存储引擎很多,目前以InnoDB最佳,算为上品.   自MySQL 5.5.5开始,InnoDB是作为默认的存储引擎,而之前MyISAM存储引擎其实也占有一席之地,但MySQL开发团队自宣布MySQL 8.0.0开发里程碑版本DMR开始,就把MySQL版本一下子从5.x跳跃到了8.0.其中的一个亮点就是事务性数

MySQL中的double write(二)(r12笔记第17天)

    MySQL里的double write是InnoDB的三大闪亮特性,另外两个是insert buffer 和自适应哈希,其实还有几个比如异步IO,Flush neighbour Page(刷新邻接页),这个和系统层面的关联性较高,所以三大亮点还是更有针对性的.    当然一说到MySQL里的double write,其实主要是要应对一个很自然的问题,那就是partial write. 经典的partial write问题    这个问题比较经典,很多数据库设计中都需要考虑到这样一个临界点

[MySQL学习] Innodb崩溃恢复流程

简要记录跟踪代码,很多代码流程没有细细的跟进去,只是了解了个大概,杂七杂八,还有太多不了解的地方. 不过,一知半解总比一无所知要好点-sign- //////////////////////////////////////////// 一.innobase_init 1.初始化存储引擎接口函数.检查指定的page大小(innodb_page_size,Percona版本支持16k以下的page size定义).innodb_log_block_size. 2.检查是否通过记录在innodb层的r

MySQL的InnoDB引擎入门学习教程_Mysql

MySQL发展到今天,InnoDB引擎已经作为绝对的主力,除了像大数据量分析等比较特殊领域需求外,它适用于众多场景.然而,仍有不少开发者还在"执迷不悟"的使用MyISAM引擎,觉得对InnoDB无法把握好,还是MyISAM简单省事,还能支持快速COUNT(*).本文是由于最近几天帮忙处理discuz论坛有感而发,希望能对广大开发者有帮助. 1. 快速认识InnoDBInnoDB是MySQL下使用最广泛的引擎,它是基于MySQL的高可扩展性和高性能存储引擎,从5.5版本开始,它已经成为了

MySQL数据库innodb启动失败无法重启的解决方法_Mysql

问题介绍 电脑在使用过程中死机,重启后发现mysql没有启动成功,查看错误日志发现是innodb出现问题导致mysql启动失败. 错误日志 $ mysql.server start Starting MySQL . ERROR! The server quit without updating PID file (/usr/local/var/mysql/fdipzonedeMacBook-Air.local.pid). 22:08:37 mysqld_safe Starting mysqld

mysql-求助大神,MySQL引擎innodb修改失败;

问题描述 求助大神,MySQL引擎innodb修改失败: 解决方案 mysql修改数据库引擎 InnoDB修改MySQL数据库引擎为INNODBmysql修改数据库的存储引擎(InnoDB)

MySQL的InnoDB扩容及ibdata1文件瘦身方案完全解析_Mysql

mysql的innodb扩容为了添加一个数据文件到表空间中,首先要关闭 MySQL 数据库,编辑 my.cnf 文件,确认innodb ibdata文件的实际情况和my.cnf的配置是否一致,这里有两种情况: 1.my.cnf的配置 innodb_data_file_path=ibdata1:10G;ibdata2:10G:autoextend 如果当前数据库正在使用ibdata1,或者使用ibdata2,但ibdata2没有超过10G,则对my.cnf配置直接改成: innodb_data_f