MySQL InnoDB Bin Log, Redo Log, Undo Log 详解

深入剖析 MySQL InnoDB 引擎中三种关键日志的工作原理、作用和实现机制

Bin log,Redo log 以及 Undo log 是 MySQL 以及 InnoDB 中较为重要的 3 中日志文件,它们帮助实现了本地事务的原子性以及持久性,同时还是复制、MVCC 等功能必不可少的组成部分。

Bin Log #

The binary log contains “events” that describe database changes such as table creation operations or changes to table data.

Bin log 记录所有对于 MySQL 执行的更改操作(包括不同存储引擎相关的操作),不记录 SELECT 和 SHOW 这类不修改数据库的操作。Bin log 产生于 MySQL 数据库上层,任何存储引擎对于数据库的更改都会产生 Bin log,包括 InnoDB、MyISAM、Heap 等任何存储引擎的⽇志。

Bin log 是逻辑日志,记录的是 SQL 语句。

Bin log 的作用 #

Bin log 的作用主要是:

  1. 恢复:数据库恢复阶段,某些数据的恢复依赖于二进制日志
  2. 复制(replication):MySQL 集群间的同步是将 bin log 中记录的数据更改复制给其它节点实现的。

From official doc MySQL :: MySQL 8.0 Reference Manual :: 5.4.4 The Binary Log:

  • For replication, the binary log on a replication source server provides a record of the data changes to be sent to replicas. The source sends the information contained in its binary log to its replicas, which reproduce those transactions to make the same data changes that were made on the source. See Section 17.2, “Replication Implementation”.
  • Certain data recovery operations require use of the binary log. After a backup has been restored, the events in the binary log that were recorded after the backup was made are re-executed. These events bring databases up to date from the point of the backup. See Section 7.5, “Point-in-Time (Incremental) Recovery”.

Bin log 的刷盘时机 #

对于 InnoDB 存储引擎而言,只有在事务提交时才会记录 bin log ,此时记录还在内存中,之后才会将将其写到磁盘之中。具体的刷盘时机是通过 sync_binlog 参数来控制的。 sync_binlog 取值范围是 0-N

  • 0:不强制要求,由系统自行判断何时写入磁盘;
  • 1:每次 commit 的时候都要将 bin log 写入磁盘;
  • N:每 N 个事务,才会将 bin log 写入磁盘。

从上面可以看出, sync_binlog 最安全的是设置是 1,这也是 MySQL 5.7.7 之后版本的默认值。但是设置一个大一些的值可以提升数据库性能,因此实际情况下也可以将值适当调大,牺牲一定的一致性来获取更好的性能。

Redo Log #

Redo log 和 bin log 不同,redo log 是 InnoDB 存储引擎文件,是在引擎层产生的。它是一种物理格式日志(和 Bin log 的逻辑日志属性不同),记录了页的修改内容。在恢复时,物理日志比逻辑日志的处理速度快许多。

Redo Log 的作用 #

为了保证事务的持久性,现代的数据库普遍采用先写日志,再做变更的策略(Write-Ahead Logging),一个事务如果将修改全部写入到了日志中,那么即便数据库宕机等意外情况,在数据库恢复阶段完全可以依照日志将修改安全地进行重做。

这也是 Redo log 名字的来源,在数据库的恢复时,数据库会判断有哪些事务已经在 Redo log 中写入了 Commit Record,这表示这些事务实际上已经完成,数据库会根据 Redo log 将这些事务未写到磁盘中的修改落盘,也因此,Redo log 实现了事务的持久性。

关于 Write-Ahead Logging 以及事务持久性的实现,可以参考笔者之前的 本地事务的原子性和持久性是如何实现的 | caffcen’s blog 及该文的参考资料作进一步的了解。

Redo log 文件以及 redo log group #

在 InnoDB 存储引擎的数据目录下会有两个名为 ib_logfile0ib_logfile1 的文件,它们既为重做日志文件。

InnoDB 存储引擎引入了重做日志组(Redo log group)的概念,每个日志组至少有 2 个重做日志文件,如默认的 ib_logfile0ib_logfile1 。为了得到更高的可靠性,可以设置多个镜像日志组(mirrored log groups)。每个日志组内的每个 redo log file 大小一致,并采用循环写(round robin)的方式进行写入。这部分之后会有详细介绍。

Redo log 格式 #

Redo log 可以看做是由位于内存中的 redo log buffer 以及磁盘中的 redo log file 这两个部分组成。它们均有 block (块)组成,称为 redo log block:

每个 redo log block 大小为 512 bytes。

Redo log block 的结构这里就不具体展开了,有兴趣的读者可以进一步阅读《MySQL 技术内幕——InnoDB 存储引擎》7.2 节的内容。

Redo log 工作原理 #

MySQL 每执行一条 DML 语句,会将页的修改记录写到内存中的 redo log buffer 之中,之后依据一定的策略将 buffer 中的记录刷到磁盘之中。

从重做⽇志缓冲往磁盘写⼊时,是按 512 个字节,也就是⼀个扇区的⼤⼩进⾏写⼊。这是因为扇区是写⼊的最⼩单位,因此可以保证写⼊必定是成功的(因此是原子的)。因此在重做⽇志的写⼊过程中不需要有 doublewrite。

MySQL 和其他进程一样,是位于用户空间中的进程,一般情况下,位于用户空间之中的内存数据是无法直接写入磁盘的,而是需要调用操作系统提供的接口,将数据拷贝到位于内核空间的缓冲区中,之后再通过 fsync 将其写入磁盘之中:

Redo log 刷盘策略 #

参数 innodb_flush_log_at_trx_commit ⽤来控制重做⽇志刷新到磁盘的策略。

参数值 含义
0(延迟写) 事务提交时不会将 redo log buffer 中日志写入到 os buffer ,而是让 master thread 来完成重入日志的落盘,因为 master thread 每秒都会将重做日志缓冲中的修改写入磁盘,无论事务是否有提交。Master thread 每秒会进行一次 fsync 操作。
1,默认值(实时写,实时刷) 事务每次提交都会将 redo log buffer 中的日志写入 os buffer 并调用 fsync 刷到 redo log file 中。这种方式即使系统崩溃也不会丢失任何数据,但是因为每次提交都写入磁盘,IO 的性能较差。
2(实时写,延迟刷) 每次提交都仅写入到 os buffer ,每秒会进行一次 fsync 。当系统崩溃时,存在于内存中未刷新到磁盘的事务数据将会丢失

innodb_flush_log_at_trx_commit 设置为 0 或 2 将破坏事务的 ACID 特性。因此为了保证事务的持久性,必须将 innodb_flush_log_at_trx_commit 设置为 1,也就是每当有事务提交时,就必须确保缓冲中的修改已经成功地写入到了磁盘中。那么,即便数据库因为各种意外发⽣宕机,也能够在重启恢复时通过 redo log 进行重做,保证了事务的持久性。(至于本地事务是如何实现持久性的,DBMS 在重启恢复阶段怎样进行重做和回滚,可以参考笔者的另一篇博客:本地事务的原子性和持久性是如何实现的 | caffcen’s blog

如果将 innodb_flush_log_at_trx_commit 设置为 0 或 2,就无法保证缓冲中的数据能够在事务提交的时候写入磁盘。0 和 2 的区别在于,设置为 2 时,当 MySQL 数据库发⽣宕机⽽操作系统及服务器并没有发⽣宕机时,内存中的数据将得到保存。当然,如果系统挂了内存中的数据当然也就丢失了。

Redo log file 写入策略 #

Redo log 采用追加(append)的方式写到 redo log file 的最后部分,当⼀个 redo log file 被写满时,会接着写⼊下⼀个 redo log file,其使⽤⽅式为 roundrobin,如下图:

在上图中, write position 表示 redo log 当前记录的 LSN (逻辑序列号) 位置, checkpoint 表示将缓存中的脏页写回磁盘后,数据页最新的版本号。

  • write positioncheckpoint 之间的部分是 redo log 空着的部分,用于记录新的记录;
  • checkpointwrite position 之间是 redo log 待落盘的数据页更改记录。

write position 追上 checkpoint 时,会先推动 checkpoint 向前移动,空出位置再记录新的日志。

Undo Log #

因为采用了 Write-Ahead Logging 策略,数据库允许事务在提交之前就将修改写到磁盘中,这提高了硬件资源的利用率(磁盘和处理器同时在进行工作),但也带来了一个问题:如果事务未能 Commit 数据库就因为某种原因宕机了,那么提前写入到磁盘中的修改就是错误的数据,事务的一致性和原子性都将被破坏。

因此,Undo log 应运而生。在数据库恢复阶段,会将那些未能完成(未在 Redo log 中写下 Commit Record)的事务(称为 Loser)对磁盘的修改进行回滚,而 Undo log 就是数据库如何进行回滚的依据。

Undo log 也是 InnoDB 存储引擎的文件,和 Bin log 一样是一种逻辑日志文件,记录了行记录变更之前的记录,也因此可以实现 MVCC(Multi-Version Concurrency Control)。

From official doc MySQL :: MySQL 5.7 Reference Manual :: 14.6.7 Undo Logs:

An undo log is a collection of undo log records associated with a single read-write transaction. An undo log record contains information about how to undo the latest change by a transaction to a clustered index record. If another transaction needs to see the original data as part of a consistent read operation, the unmodified data is retrieved from undo log records. Undo logs exist within undo log segments, which are contained within rollback segments. Rollback segments reside in the system tablespace, in undo tablespaces, and in the temporary tablespace.

总结一下,Undo log 的作用主要有两个:

  1. 回滚
  2. MVCC

Undo log 的存储 #

Redo log 和 bin log 都以日志文件的形式进行存储,undo log 则不同,undo 存放在数据库内部的 undo 段中。 undo 段位于共享表空间、undo 表空间及临时表空间中。

Undo logs exist within undo log segments, which are contained within rollback segments. Rollback segments reside in the system tablespace, in undo tablespaces, and in the temporary tablespace.

Undo log 的具体内部结构以及相关的 purge 操作,可以参考《MySQL 技术内幕——InnoDB 存储引擎》7.2.2 和 7.2.3 节,在此不再赘述。

Summary #

Bin Log Redo Log Undo Log
作用 ① 复制(Replication) ② 恢复 数据库恢复时用于重做事务 ① 回滚 ② MVCC
文件分类 MySQL 数据库逻辑日志文件 InnoDB 引擎物理日志文件 InnoDB 引擎逻辑日志文件
记录内容 SQL 语句 页的修改 SQL 语句

Reference #

  1. 《MySQL 技术内幕——InnoDB 存储引擎》3.2, 3.6, 7.2
  2. 本地事务 | 凤凰架构
  3. MySQL :: MySQL 8.0 Reference Manual :: 5.4.4 The Binary Log
  4. MySQL :: MySQL 8.0 Reference Manual :: 15.6.5 Redo Log
  5. MySQL :: MySQL 5.7 Reference Manual :: 14.15 InnoDB Startup Options and System Variables
  6. MySQL :: MySQL 5.7 Reference Manual :: 14.6.7 Undo Logs
  7. 彻底搞懂MySQL的redo log,binlog,undo log - 掘金