在更新完缓存页之后,必须写一条redo log,记录对数据的修改。
redo log 可以保证事务提交后,即使mysql宕机,也可以恢复缓存页。
通过 redo log,可以保证事务提交后,修改的数据不会丢失。

为何使用 redo log 而不直接刷磁盘

redo log 的日志格式:
对表空间x1中的数据页x2中偏移量为x3的地方更新了数据x4。
// 这里涉及到mysql 物理存储结构。step3

核心问题:为什么需要redo log?
缓存页默认大小是16kb,因为缓存页所缓存的数据页在磁盘上随机分布,所以刷磁盘是随机写的操作。如果修改了一行数据,就重新刷缓存页到磁盘,那么会由于随机写磁盘而导致性能降低。

redo log可能就几十个字节,包含表空间号,数据页号,磁盘文件偏移量,更新值等。
刷redo log到日志文件的操作,是顺序写操作,因此写入磁盘的速度很快。

因此,提交事务时,用redo log的格式记录所做的修改,性能会远远超过刷缓存页的方式,使用redo log,可以大幅提高数据库处理并发请求的能力。

redo log 的格式

日志类型、表空间ID、数据页号、数据页中的偏移量、具体修改的值

首先需要明确的是,redo log的目的是能精确还原增删改操作所做的变动。
因此需要记录变动位置:表空间ID、数据页号、偏移量等。
表空间ID,对应于SQL在哪个表里执行的;数据页号,对应磁盘的数据页。

其次是,redo log的设计,需要进行性能优化的考量,比如这里的日志类型。
根据你修改了数据页里的几个字节的值,redo log就划分为了不同的日志类型,MLOG_1BYTE表示修改了1个字节;MLOG_2BYTE表示修改了2个字节
还有修改了4字节、8个字节的值的日志类型。
MLOG_WRITE_STRING类型的日志,会有一个“修改数据长度”的字段。

redo log buffer & block

并不是来了一条 redo log就刷磁盘,而是先存到 redo log block 里。

redo log buffer 是一片连续内存,大小是 innodb_log_buffer_size,一个 redo log buffer 里有多个 block。
每个 block 有512 字节,包含header,body,tailer。

redo log block
截屏2021-08-20 上午9.11.04.png

一条事务可能产生多条 redo log,这些 redo log 可能存在于多个 block。
多个事务的 redo log 也可能存在于同一个 redo log block。
截屏2021-08-20 上午9.09.51.png

redo log 刷磁盘的时机

三种时机:
1 当 buffer 中的日志占据超过50%。
此情况发生于mysql处理高并发请求,产生大量 redo log的场景。

2 当事务提交时,该事务所在的redo log都要刷到磁盘。
// 刷盘时也是先进入os cache,然后刷到磁盘文件
一般来说一个事务是在几十/几百ms内完成的,事务提交时,立马把redo log刷磁盘。

3 后台线程每隔1s刷 redo log block 到磁盘文件。

4 mysql 关闭时

undo log 的含义

理解了 buffer pool 和 redo log,再看 undo log就很简单。

如果在缓存页执行 insert 语句,undo log 记录主键和 delete 操作。
如果在缓存页执行 delete 语句,undo log 记录主键和 insert 操作。
如果在缓存页执行 update 语句,undo log 记录主键和 update 操作。

一个事务包含若干 SQL 语句,每条 SQL 语句都有对应的 undo log。
根据 undo log,可以恢复到修改前的状态。

undo log 格式

INSERT语句的undo log的类型是TRX_UNDO_INSERT_REC,这个undo log里包含了以下内容:

  • 日志的开始位置
  • 主键的各列长度和值
  • 表id
  • undo log日志编号
  • undo log日志类型
  • 日志的结束位置

每个日志都有一个编号,一个事务里可能有多条SQL语句,对应多条 undo 日志。