redo log

MySql有两个重要的日志模块,binlog 和 redo log,引入redo log主要为了故障恢复和提升更新效率。思路是先内存后磁盘,借助另外机制将随机读写改为顺序读写。

WAL 技术

WAL技术全称Write-Ahead Logging,思路是先写日志,再写磁盘。MySql使用redo log也是使用了这种方式,InnoDB引擎会先把记录写到redo log中,然后更新内存,此时更新已经完成。InnoDB引擎会在适当时候把脏页刷到磁盘。

此时涉及脏页有两类:数据页和redo log日志页。数据页是MySql的表数据(MySql中的数据按照页为单位读取,一个页大小16KB,如果数据行记录越小,能从磁盘读取到内存的行记录数越多),redo log日志页是redo log的脏页,记录redo log同样有内存与磁盘,在MySql的Server层Buffer Pool外有一块内存是log Buffer,存放redo log的记录,并按一定时机刷新到磁盘,磁盘对应的文件为数据文件的ib_logfile,ib_logfile文件有多个,可以通过配置文件innodb_log_files_in_group指定个数,默认值为2,最大值为100;innodb_log_file_size可以指定每个redo日志文件的大小。并且为防止磁盘碎片,每个ib_logfile在启动后就提前申请了磁盘空间。

更新redo log的脏页的效率比更新内存中数据页高,原因是:MySql 中索引树的页节点都是分散在磁盘各个位置,每次更新可能需要更新多个页面,那么这样一次更新会涉及多个随机读,而更新数据页对于内存和redo log都是更新内存,redo log的log buffer在内存中是连续的并且利用预先生成的多个ib_logfile的连续空间,磁盘文件也是顺序写。

有些系统也使用了WAL(预写日志)的策略,在内存中进行页变更但是不立即刷新到磁盘,因为刷磁盘将带来随机IO。如果变化的记录写到一个连续的日志文件中,更新速度就会加快。后台线程可以稍后把修改的页面刷新到磁盘。通过这种方式将随机IO改为了顺序IO。

刷脏页时机

  • log buffer空间不足

log buffer的大小通过系统变量innodb_log_buffer_size指定,如果不停的往log buffer里写入日志,很快它就会被填满。而触发脏页刷新到磁盘。

  • 后台线程

后台有一个线程,定期刷新log buffer中的redo日志到磁盘。

  • 正常关闭服务器

类似许多应用都要做的,当收到kill信号时温柔的关闭应用。

另外,事务提交时也有对应的刷脏页时机,如下:

innodb_flush_log_at_trx_commit这个系统变量值可以控制事务提交时刷脏页的时机,如果事务的物理修改都记录到log Buffer中,那么相对于磁盘log Buffer中的数据页就是脏页,需要在某个时刻刷到磁盘。通过参数innodb_flush_log_at_trx_commit可以控制这个时刻,

  • 0:当值为0时,表示在事务提交时不立即向磁盘中同步redo日志,这个任务是交给后台线程做的(能提升修修改速度,但是如果宕机时数据页的修改还在内存中会导致修改丢失)。
  • 1(默认值):表示在事务提交时需要将redo日志同步到磁盘,可以保证事务的持久性。
  • 2:表示在事务提交时需要将redo日志写到操作系统的缓冲区中,但并不需要保证将日志真正的刷新到磁盘(如果数据库宕了,操作系统没宕,事务的持久性还是可以保证)。

crash-safe

崩溃恢复是redo log的一大功能,这个功能是binlog没有的,有了 redo log,InnoDB 就可以保证即使数据库发生异常重启,之前提交的记录都不会丢失。由于redo log对应的磁盘文件组ib_logfile*由几个固定大小的文件组成,并且采用循环写的方式,所以对应的redo log是可被覆盖的,如果redo log中记录的数据页脏页已经刷新到了磁盘,那么redo log之前的空间就可以被重复使用。在redo log中有个checkpoint中记录了可以被覆盖了位置,checkpoint_lsn之前的redo日志都可以被覆盖。checkpoint_lsn之后的redo日志,对应的脏页并不确定有没有被刷盘,所以需要从checkpoint_lsn开始读取redo日志来恢复页面。

如上图,write pos 是当前记录的位置,一边写一边后移,写到第 3 号文件末尾后就回到 0 号文件开头。checkpoint 是当前要擦除的位置,也是往后推移并且循环的,擦除记录前要把记录更新到数据文件。write pos 和 checkpoint 之间的部分,可以用来记录新的操作。如果绿色部分满了,这时就需要推进checkpoint,以使得有空间存放新的脏页记录,而推进的方式就是将推进所经过的数据脏页记录刷新到磁盘,这时如果是一条普通的select触发了这个刷新,则会使此次查询速度相比之前都慢。所以checkpoint可以说是恢复的起点

每个ib_logfile*文件由多个block组成,写满一个会继续下一个,普通block的log block header部分有一个称之为LOG_BLOCK_HDR_DATA_LEN的属性,记录了当前block里使用了多少字节的空间。对于被填满的block,该值永远为512。如果该属性的值不为512,那么block对应的位移就是恢复的终点

如下图,来自掘金小册:

当确定了恢复范围后,就可以按照redo日志的顺序依次扫描checkpoint_lsn之后的各条redo日志,按照日志中记载的内容将对应的页面恢复出来。

正常数据页流程

如果数据页被修改,跟磁盘的数据页不一致,那么内存中的数据页就是脏页,最终数据落盘,就是把内存中的数据页写盘。这个过程与 redo log无关。在崩溃恢复的场景中,InnoDB 如果判断到一个数据页可能在崩溃恢复的时候丢失了更新,就会将它读到内存,然后应用一下 redo log 中对该数据页的物理修改,当应用完redo log后内存中的数据页和磁盘又不一致了,所以内存中又回到了脏页状态,继续数据落盘,就是把内存中的数据页写盘。这个过程也与 redo log无关。

参考

最后修改日期: 2021年2月3日

作者

留言

撰写回覆或留言

发布留言必须填写的电子邮件地址不会公开。