1. redo log 存在的意义

在执行增删改操作的时候,首先会在 Buffer Pool 中更新缓存页,接着必须要写一条 redo log,这样才能记录下来对数据库的修改。
redo log 可以保证我们事务提交之后,如果事务中的增删改SQL语句更新的缓存页还没有刷新到磁盘上去,此时MySQL 宕机了,那么MySQL 重启之后,就可以把 redo log 重做一遍,恢复出来事务当时更新的缓存页,然后再把缓存页刷新到磁盘上就可以了。
redo log 本质就是保证事务提交之后,修改的数据绝对不会丢失。

在执行增删改SQL语句的时候,都是针对一个表中的某些数据去执行的,此时首先必须找到这个表对应的表空间,然后找到表空间对应的磁盘文件,接着从磁盘文件里把你要的那批数据页从磁盘读取出来,放到 Buffer Pool 的缓存页里去,如下图示所示。
MySQL 加载数据简化流程.png

接着执行的增删改SQL语句都是针对 Buffer Pool的缓存页进行操作,比如插入一行数据,或者更新、删除一行数据。
MySQL 加载数据、操作简化流程.png
在更新缓存页的时候,会更新 free 链表,flush 链表,lru 链表,然后有专门的后台 IO 线程,不定时的根据flush 链表、lru 链表,会把你更新过的缓存页刷新到磁盘文件的数据页中,如下图说是。
MySQL 加载数据、操作简化流程 (1).png

整个过程最大的漏洞就是万一你一个事务有增删改SQL更新了缓存页,然后事务提交了,结果万一你还没来得及让IO 线程把缓存页刷新到磁盘文件里,此时MYSQL宕机了,然后内存数据丢失,事务更新的数据就丢失了。
redo log 机制就是将你修改操作以日志的方式写入到 redo log日志,哪怕你此时宕机。重启后也会将Redo log 中的操作重新执行一遍,恢复当时的修改。

那为什么不直接将缓存页刷入到磁盘中,而是将修改写入到redo log 中,换句话说这两种都是磁盘操作有何区别。实际上,有两个原因:
第一个原因,是缓存页一般比较大,以默认的16KB为例,每次修改几个字节却要将整个缓存页刷入到磁盘不合理。而一条redo日志基本上只有几十字节。
第二个原因,是缓存页刷入到磁盘是随机读写,性能很差。而redo log 日志是顺序写入到磁盘文件中,每次追加到文件末尾。

2.redo log 格式

redo log 本质上记录的就是对某个表空间的某个数据页的某个偏移量的地方修改了几个字节的值,具体修改的值是什么。所以它的内容包括表空间号 + 偏移量 + 修改几个字节的值 + 具体的值。
根据你修改值的个数,redo log 划分为不同的类型,MLOG_1BYTE、MLOG_2BYTE 等等。当然如果修改的值比较多,类型就是 MLOG_WRITE_STRING。
所以一条redo log 大致结构如下:

  1. 日志类型(例如MLOG_2BYTE),表空间ID,数据页号,数据页中的偏移量,具体修改的值

有了上述的信息,就可以完整的还原出一次操作的内容。
如果是MLOG_WRITE_STRING类型的数据,由于不知道具体修改了多少字节的偏移量,就会在后边的字段中加上长度的字段,它的格式大概如下

  1. 日志类型(例如MLOG_2BYTE),表空间ID,数据页号,数据页中的偏移量,修改数据长度,具体修改的值

3. redo log block

为了提高性能,redo log 写入时并不是一行一行写入的,它是一个block写入的。一个redo block 大小是 512字节,这个redo log block 字节分为 3个部分,一个是12字节的 header 部分,一个496字节的 body 块体,一个是4字节的 tariler 块尾。
redo log 格式.png
其中,12字节的 header 头又分为 4 个部分。
(1)包括4个字节的 block no,就是块唯一的编号
(2)2个字节的 data length,就是block 里写入了多少字节的数据
(3)2个字节的 first record group。每个事务都会有多个 redo log, 是一个 redo log group,即一组 redo log。那么在这个block 里第一组 redo log 的偏移量,就是2个字节存储的
(4)4个字节checkpoint on
redo log 格式 (1).png
redo log 写入到 日志文件中的方式,如下图所示。
redo log 格式 (3).png

4. redo log buffer

redo log buffer 其实就是MySQL 在启动的时候,跟操作系统申请的一块连续的内存空间,大概可以认为相当于buffer pool。它会在内存中划分N个空的 redo log block。
通过设置 mysql 的 innodb_log_buffer_size 可以指定这个redo log buffer 大小,默认值就是 16MB。
当你写一条redo log 的时候,就会先从第一个 redo log block 开始写入。如下图
redo log 格式 (4).png

写满一个 redo log block 就会继续写下一个 redo block ,以此类推,直到所有的 redo log block 都写满,就会强制把 redo log block 刷入到磁盘 中去。

平时在执行一个事务的过程中,每个事务都会有多个增删改操作,那么就会有多个 redo log,这多个 redo log 就是一组 redo log,其实这一组redo log 都是现在一个地方暂存,然后都执行完,再把一组 redo log 写入到 redo log buffer 的 block 里去的。
如果一组redo log 实在太多了,那么可能会存放在多个 redo block 中,相反很少,那么可能多个redo log group 存放在一个 redo log block 里面。

5. redo log 刷入磁盘时机

redo log block 哪些时候会刷入到磁盘文件中去。
(1)如果写入 redo log buffer 的日志已经占据 redo log buffer 总容量的一半,也就是超过了8MB的 redo log 在缓冲里了,此时就会把他们刷入到磁盘文件里去
(2)一个事务提交的时候,必须把他的那些 redo log 所在的 redo log block 都刷入到磁盘文件里去,只有这样,当事务提交之后,他修改的数据绝对不会丢失,因为redo log 里重做日志,随时可以恢复事务做的修改。当然它会先刷入os cache 中,但是有参数可以控制强制刷入到磁盘中。
(3) 后台线程定时刷新,有一个后台线程每隔1秒钟就会把 redo log buffer 里的 redo log block 刷入到磁盘文件里去
(4)MySQL 关闭的时候,redo log block 都会刷入到磁盘里去

redo log 可以有几个。
如果不停的写入文件会越来越大。其实在 mysql 中是写到 innodb_log_files_in_group 目录中,默认是2个文件,ib_logfile0和ib_logfile1,每个文件48MB,写完一个文件覆盖另外一个,保留最近的96MB日志内容。通常一条日志就几个日志到几十个字节不等,96MB足够存储上百万条redo log 。
如果你还想保留更多的 redo log,调节上述的两个参数就可以了,比如每个 redo log 文件是 96MB,最多保留 100个 redo log 文件。