redo log

redo log 是什么?

InnoDB 是以页为基本单位来管理内存空间,为了减少磁盘 IO 的开销,增加了 Buffer Pool,每次访问页面后不着急刷新回磁盘,而是缓存到 Buffer Pool 中,以便下次继续访问

Buffer Pool 在内存中,所以会导致后续对页面的修改并不能及时刷新回磁盘,如果发生断电或者一些其它的原因会使内存中的数据丢失,重启后啥也没了,必须持久化到磁盘才安全

一个很简单的方法就是每次修改后立刻将页面刷新回磁盘,这样做的弊端在于:

基于上面的场景,就有了 redo log (重做日志),它记录了对哪个表空间哪个页进行了哪些修改,可以简单理解为就算数据丢失,但只要有对应的 redo log,也可以恢复丢失的数据

redo log 也需要及时的刷新回磁盘,但它相比于刷新整个页面的优势在于:

redo log 写入过程

为了更好的管理 redo log,InnoDB 将 redo log 存储到大小为 512 字节的 block 中 (block 可以理解为页) 的 log blcok body 部分。每个 block 的结构如下所示:

15

和 Buffer Pool 一样,为了缓解磁盘 IO 速度慢的问题,InnoDB 引入了 log buffer,它是内存中一片连续的区域,默认大小为 16MB

redo log 是顺序写入 log buffer 中,从前往后,有一个 buf_free 指向空闲区域的起始位置,具体结构如下图所示:

16

redo log 刷盘时机

log buffer 在内存中,如果不及时的将 log buffer 刷盘,也会导致 redo log 丢失,所以 log buffer 会在出现下面情况时刷盘

对于上面第二点刷盘情况:事务提交时,由于这个要求过于严格,会降低数据库新能,所以可以通过参数innodb_flush_log_at_trx_commit调整:

上面三个参数的安全性和性能的关系:

重点:对于安全性要求高的系统,只能选择 1;对于可以接受 1s 数据丢失的信息,可以选择 0;如果既要求性能也要保证一定的安全性,可以选择 2,折中方案

redo log 文件组

当 log buffer 触发刷盘时,会存到 redo log 文件中,它是以日志文件组的形式出现,即:一组中有多个大小相等的日志文件,默认情况下有两个文件:ib_logfile0 和 ib_logfile1

redo log 是为了记录内存中还没有刷盘的脏页,防止当 MySQL 挂了后脏页丢失,导致修改数据的丢失。所以当脏页已经被刷盘,那么对应的 redo log 就没有用

随着系统的运行,redo log 只会越来越多,而日志文件组的数量和大小都是固定的,所以它采用循环写的方式,从头开始写,当写到末尾又回到开头,这样可以在有限的日志文件组中无限的写入 redo log

之所以可以覆盖日志文件组开头的数据,是因为开头的数据可能会随着脏页的刷盘而变成无用。用 write pos 表示新 redo log 刷盘时要写入的位置;用 checkpoint 表示当前要擦除的位置

17

图中:

当 write pos 追上 checkpoint 时,表示 redo log 文件组满了,不能再将 redo log 刷盘到文件组中

此时 MySQL 会阻塞,将 Buffer Pool 中的脏页刷新到磁盘,然后标记 redo log 文件组可以被擦除的部分,最后将可以被擦除的部分擦除,更新 checkpoint (顺时针向前移动)

所以,一次 checkpoint 的过程就是将脏页刷新到磁盘中,更新 redo log 日志组中可以被覆盖的位置