一直以来我都有个疑问,双写缓冲区存在的必要性,我知道Redo log是用来保持数据持久性一致性的,觉得好像没必要再来个双写缓冲区。
后来又因为双写缓冲区的内容是脏页的副本。这时候我就觉得Redo log是多余的,完全可以用双写缓冲区来代替Redo log实现宕机后数据的持久性。
如果没有redo log和双写缓冲区:
以 UPDATE t SET c1=10 WHERE id=1为例,磁盘上的数据页记录为c1=5
innodb开启一个事务,
1.先查询buffer pool中,未发现数据,它会将磁盘上的数据页加载到buffer pool中,buffer pool中即存在数据c1=5
2.执行事务修改数据,将buffer pool中的数据修改成了c1=10,此时buffer pool中的这个数据页就变成了脏页
3.过了一段时间,准备将脏页数据写入到磁盘
此时会发生的情况包括:
1.脏页还没开始写,如果宕机了,事务的持久性没有了,修改全部失效了(相当于有双写缓冲区,没有Redo Log的情况)
ok,让我们来解决这个问题,在脏页还没开始写之前,我们在事务提交之后先记录一个文件,就叫做redo log吧,这样就算在宕机了,我们还可以从redo log中恢复事务修改的数据。如果redo log记录失败,那么我们就当是事务执行失败了,也就不需要恢复了,当然这种几率相比较脏页直接写的几率低很多,而且因为redo log是顺序写,比脏页的随机刷盘要快很多。
2.脏页写到一半,如果宕机了,数据页的完整性也没有了(相当于有Redo Log,没有双写缓冲区的情况)
因为redo log的重写操作的前提需要数据页保持完整,这时候我们可以在脏页刷盘之前,先不操作原来的数据页,先在一个临时的连续的磁盘空间(也就是双写缓冲区)中存储一下要修改的脏页内容。如果存储成功再修改原来的数据页,这样就算原来的数据页写到一半失败了,也可以从这个临时空间中进行恢复。如果这个临时空间记录到一半呢?那么就会丢弃这段记录,但是至少原来的数据页是完整对吧。
针对redo log为什么需要数据页保持完整,详情查看另一篇文章Redo Log 对于不完整的数据页不能直接覆盖写吗
1.如果是单纯的逻辑不一致,比如某条数据的部分字段修改了,部分字段未及时修改,那可以靠redo log恢复
下面两种数据页不完整的情况是无法用redo log来恢复的
1.如果数据页的页头页尾的元数据未能正确写入,导致innodb无法识别页的有效性
2.如果磁盘故障,导致数据页内容全部损坏,如乱码或者全0
以下时一个正常的流程:
以 UPDATE t SET c1=10 WHERE id=1为例,磁盘上的数据页记录为c1=5
innodb开启一个事务,
1.先查询buffer pool中,未发现数据,它会将磁盘上的数据页加载到buffer pool中,buffer pool中即存在数据c1=5
2.执行事务修改数据,将buffer pool中的数据修改成了c1=10,此时buffer pool中的这个数据页就变成了脏页
3.写redo log, 此时redo log之中的数据变成了c1=10
4.写双写缓冲区,此时双写缓冲区变成了c1=10
备注:双写缓冲区是在脏页刷盘之前会进行的操作,双写缓冲区相当于脏页的备份,当脏页刷盘失败的时候,此时是宕机状态。数据库要从宕机状态中恢复过来,会先去查看redo log日志,其中redo log日志的头部会包含checkpoint LSN(checkpoint LSN直译过来就是检查点 日志序列号,也就是记录了磁盘数据页同步数据同步到redo log的哪一行了,当脏页刷盘成功的时候,就会更新这个编号),如果checkpoint LSN小于redo log最新的行号,说明数据库宕机的时候还有数据未持久化完。这时候就会开始进行执行redo log未执行完的操作,找到相关的数据页,在找到的时候,会进行checksum校验和的检查,这是每个数据页的最尾端记录的,用于校验每个数据页的完整性的,如果校验失败,则会去通过双写缓冲区进行恢复。
5.写磁盘,也就是脏页的刷盘,如果刷盘成功,则数据页的数据变成c1=10
备注:如果刷盘失败后,数据页不完整,那至少双写缓冲区是完整的,因为双写缓冲区写完了才会开始写数据页。
备注:什么时候会开始脏页刷盘呢?详情见另一篇文章在 MySQL 的 InnoDB 存储引擎中,脏页(Dirty Page)的刷盘(Flush)时机
大致的话,分为
1.InnoDB 的 Page Cleaner 线程 会周期性地将脏页刷入磁盘,防止内存中脏页堆积。(buffer pool中的脏页比例达到阈值,默认90%|系统空闲时,后台线程主动刷盘减少突发IO的压力)
2.Redo Log的日志空间不住,需要推进Checkpoint LSN
3.Buffer pool需要引入新的数据页,内存不够用了,所以需要把脏页刷盘
4.数据库正常关闭的时候,重启的时候