当前位置: 首页 > 科技观察

讲讲 MySQL 中的 Wal 策略和 CheckPoint 技术

时间:2023-03-21 22:00:08 科技观察

谈谈MySQL中的Wal策略和CheckPoint技术转载本文请联系飞天小牛公众号。前段时间,我在准备暑期实习。这是我面试携程的时候问过双方的问题。我很困惑并迅速道歉。对不起,我没听懂。面试官又解释了redolog。我在想。重做日志我知道,什么是WAL?被面试官无语了(滑稽),为自己当时的无知深表歉意。后来回百度了一下,发现最近在丁奇的?看到了WAL,于是写了一篇总结一下。InnoDB架构在说WAL之前,有必要先简单介绍一下InnoDB存储引擎的架构,以便我们了解下面的内容,而redolog也是InnoDB存储引擎独有的。如下图所示,InnoDB存储引擎由一个内存池和一些后台线程组成:内存池先解释一下内存池。首先,我们要知道InnoDB存储引擎是基于磁盘存储的,对其中的记录进行分页管理。所以把它想象成一个基于磁盘的数据库系统(Disk-baseDatabase),在这样的系统中,由于众所周知的CPU速度和磁盘速度的不匹配,缓冲池技术通常被用来提高整体性能数据库。所以这里的内存池也称为缓冲池(理解为缓存即可)。具体来说,缓冲池其实就是一块内存区域。在CPU和磁盘之间加入内存访问,利用内存的速度来弥补磁盘速度慢对数据库性能的影响。有了缓冲池后,“读取页面”操作的具体步骤如下:首先将从磁盘读取的页面存储在缓冲池中,下次读取相同的页面时,先判断该页面是否在缓冲区中水池。如果在bufferpool中,则称page在bufferpool中命中,直接读取page。否则,读取磁盘上的页面。“修改页面”操作的具体步骤如下:首先修改缓冲池中的页面;然后以一定的频率刷新到磁盘。所谓的“脏页”发生在修改操作中。如果缓冲池中的页面已经被修改但还没有被刷入磁盘,那么我们称缓冲池中的页面为“脏页”,即缓冲池中页面的版本比新的磁盘上的版本。至此,综上所述,我们可以得出结论,缓冲池的大小直接影响数据库的整体性能。后台线程后台线程最大的作用就是完成“将从磁盘读取的页面存储到缓冲池中”和“以一定的频率将缓冲池中的数据刷新到磁盘中”这两个操作。是的,还有其他功能。以下是《MySQL 技术内幕:InnoDB 存储引擎 - 第 2 版》对后台线程的描述:后台线程的主要作用是刷新内存池中的数据,保证内存池中缓存的是最新的数据;InnoDB可以在数据库出现异常时恢复正常运行。另外,InnoDB存储引擎是多线程模型,也就是说它有多个不同的后台线程,分别负责处理不同的任务。下面介绍几种不同的后台线程:MasterThread:主要负责将缓冲池中的数据异步刷新到磁盘,保证数据的一致性IOThread:AIO(AsyncIO)在InnoDB存储引擎中广泛使用,用于处理WriteIO请求,可以大大提高数据库的性能。IOThread的工作主要是负责这些IO请求的回调(callback)处理。PurgeThread:回收已经使用和分配的undopages。PageCleanerThread:将之前版本的脏页刷新操作放到一个单独的线程中。结束。其目的是减少原来MasterThread的工作量和阻塞用户查询线程,进一步提高InnoDB存储引擎的性能。重做日志和WAL策略上面我们提到,当缓冲池中的一个页面被修改时,该页面被标记为“脏页”,脏页的数据会被周期性的刷新到磁盘中。如果每次页面发生变化,都将新页面的版本刷入磁盘,那么这个开销是非常大的。而且,如果热点数据集中在少数几页,那么数据库的性能会变得很差。此外,如果在将新版本的页面从缓冲池刷新到磁盘时发生崩溃,则无法恢复数据。因此,为了避免数据丢失的问题,目前的事务型数据库系统(非MySQL独有)普遍采用WAL(WriteAheadLog,预写日志)策略:即在事务提交时,重做日志(重做日志)是最先写入的。log),然后修改页面(先修改缓冲池,再刷新到磁盘);当由于宕机导致数据丢失时,通过重做日志完成数据恢复。这也是事务性ACID中D(Durability持久化)的要求。有了redolog,InnoDB可以保证即使数据库异常重启,之前提交的记录也不会丢失。这种能力称为碰撞安全。举个简单的例子,假设你很热情,很有钱,借了很多钱,但是你很守旧,不会用电子设备,记性不好,所以你用的是小笔记本写下所有欠你的钱人名和具体数额。这样,别人还你的时候,你就得翻出你的小本子,一页一??页的找出他的名字,然后扣掉你这次还的钱。但是,其实你平时很忙,没法随时随地翻着小本子做记录,于是你萌生了一个想法:每当有人还钱给你,你就记在一张纸上。白纸,然后挑个时间对照小本本,清空白纸上的所有账目。这是WAL。白皮书就是redolog,小本子就是磁盘。当然,重做日志不是一张白纸那么简单。用完后只需更换一张。这里有必要详细说明一下。每个InnoDB存储引擎至少有1个重做日志文件组(redologgroup),每个文件组至少有2个重做日志文件(redologfile),默认为一个重做日志组,其中包含2个重做日志文件:ib_logfile0和ib_logfile1。一般来说,为了获得更高的可靠性,用户可以设置多个镜像日志组(mirroredloggroups),将不同的文件组放在不同的磁盘上,从而提高重做日志的高可用性。日志组中每个重做日志文件的大小相同,并以循环写入的方式运行。所谓循环写入就是为什么我们说redolog不需要像一张白纸一样用完换掉。例如,如下图所示,一个重做日志组包含3个重做日志文件:InnoDB存储引擎会先写入重做日志文件0,当文件0写满时,会切换到重做日志文件1,当文件1满时也满了,会切换到重做日志文件2,当文件2也满了,又会切换到文件0。可见重做日志文件的大小设置对InnoDB存储引擎的性能影响很大:重做日志文件不能设置太大,如果设置太大,可能会耗费很长时间restoreredologfile并且不能设置的太小,否则可能会导致一个事务的日志多次切换redologfiles。CheckPoint技术能否让redolog高枕无忧?显然没那么简单,我们还面临三个问题:1)Buffering池不是无限的,也就是说我们不能无休止的存储我们的数据,等着一起刷新到磁盘上,那么当所有的时候怎么办重做日志文件已满?3)当数据库已经运行了几个月甚至几年,如果此时出现宕机,需要很长时间才能重新应用redolog。价格会非常高。因此,Checkpoint技术的目的就是解决以上问题:当缓冲池不够时,flushdirtypagestodisk。当重做日志不可用时,将脏页刷新到磁盘以缩短数据库的恢复时间。所谓CheckPoint技术,简单来说就是在重做日志文件中找到一个位置,将这个位置之前的页面刷新到磁盘中。这个位置叫做检查点(checkpoint)。以上三点我们依次解释一下:1)缩短数据库的恢复时间:当数据库宕机时,数据库不需要重做所有的日志,因为checkpoint之前的page已经flush回磁盘了。因此数据库只需要在Checkpoint之后恢复redolog即可。这显然大大缩短了恢复时间。2)当bufferpool不够时,flushdirtypagestodisk:所谓bufferpool不够,就是bufferpool中的空间不能存放新读取的pages。这个时候InnoDB引擎会做什么呢?LRU算法。InnoDB存储引擎对传统的LRU算法做了一些优化,用于管理缓冲池的空间。总体思路还是传统的LRU集合,具体的优化细节这里不再赘述:即使用频率最高的页面在LRU链表(LRUList)的前端,使用频率最低的页面在LRU链表结束;当缓冲池中的LRU列表中的空间无法容纳新读取的页面时,LRU列表末尾的页面将首先被释放。如果释放(溢出)的页面是脏页,则需要强制CheckPoint将脏页刷新到磁盘。3)当redolog不可用时,flushdirtypagestodisk:所谓不可用的redolog就是所有的redolog文件都满了。但实际上,重做日志中的数据并不总是有用的。那些不再需要的部分称为“可重用部分”,即当数据库宕机时,数据库恢复操作不会需要重做日志的这部分,所以这部分可以被覆盖重用(或擦除).举个例子详细解释一下:一组4个文件,每个文件大小为1GB,那么总共有4GB的重做日志文件空间。writepos是当前重做日志记录的位置。随着磁盘的不断写入,writepos也向后移动。上面我们说了,写到文件3的末尾后,会回到文件0的开头。CheckPoint是当前要擦除的位置(刷新Checkpoint回盘前的页),也是向后移动并循环:在writepos和CheckPoint之间是redolog文件上还空着的部分,可以用来记录新的操作。如果writepos赶上了CheckPoint,说明redolog文件满了。此时,无法执行新的更新。您必须先停止并覆盖(擦除)一些重做日志,然后将CheckPoint向前推。总结一下,Checkpoint所做的无非就是将缓冲池中的脏页刷新到磁盘中。区别在于每次刷新多少页到磁盘,每次从哪里取脏页,什么时候触发Checkpoint。InnoDB存储引擎内部,有两种Checkpoints,分别是:SharpCheckpoint:当数据库关闭时,将所有脏页刷新回磁盘。这是默认的工作方法。参数innodb_fast_shutdown=1FuzzyCheckpoint:在InnoDB存储引擎内部使用这种模式,只刷新部分脏页,而不是将所有脏页刷新回磁盘。FuzzyCheckPoint的具体情况这里不再赘述。有了binlog,为什么还需要redolog?前面我们说过,MySQL的架构可以分为两层,一层是Server层,主要做MySQL功能层面的事情;另一层是存储引擎,负责存储和提取相关的具体事务。redolog是InnoDB引擎特有的日志,server层也有自己的日志,包括错误日志(errorlog)、二进制日志(binlog)、慢查询日志(slowquerylog)、查询日志(log).其他三个日志很容易理解。需要说明的是binlog(二进制日志,binarylog),它记录了所有对MySQL数据库进行更改的操作,但不包括SELECT、SHOW等操作,因为这类操作本身并没有修改数据。也就是说binlog是逻辑日志,记录了这条语句的原始逻辑,比如“给ID=1的行的a字段加1”。可见binlog日志只能用于归档,所以binlog又叫归档日志。显然,如果MySQL仅仅依赖于binlog等这四种类型的日志,是没有crash-safe能力的,所以为了弥补这种先天的不足,就需要借助MySQL的可插拔存储引擎架构,开发了InnoDB另一套日志系统——即重做日志来实现崩溃安全能力。这就是为什么需要binlog和redolog的答案。回顾一下redolog存储的东西,我们可以发现redolog是一个物理日志,记录的是“某个数据页发生了哪些变化”。另外,还有一个区别:binlog是追加写入的,也就是说binlog文件写入到一定大小后,会切换到下一个,不会覆盖之前的日志;而重做日志是循环写入的。