MySQL 中的重做日志和二进制日志

重做日志 redo log 重做日志是用来保证事务的持久性的,它体现了 MySQL 中的 WAL 技术,即在持久化数据之前,先保证 redo log 已经写到了磁盘上。重做日志是基于磁盘的数据结构,记录了在某个数据页上做了什么修改,是 InnoDB 引擎特有的日志,在事务提交前重做日志被写入磁盘,当系统崩溃时,就可以根据 redo log 来恢复数据。

WAL:Write-Ahead Logging,先写日志,再写磁盘。
当执行一条更新语句时,InnoDB 首先会将更新信息写入重做日志中,然后将重做日志放到重做日志缓冲区(redo log buffer),之后按照一定的频率将 redo log buffer 写入重做日志文件中。
MySQL 中的重做日志和二进制日志
文章图片

重做日志对应的物理文件
默认情况下,在数据库的 data 目录下有两个文件 ib_logfile1ib_logfile2。重做日志文件以循环写入的方式进行:
MySQL 中的重做日志和二进制日志
文章图片

重做日志缓冲区刷新到磁盘
【MySQL 中的重做日志和二进制日志】按照下面三种情况刷新:
  1. Master Thread 每一秒将重做日志缓冲刷新到重做日志文件;
  2. 每个事务提交时会将重做日志缓冲刷新到重做日志文件;
  3. 当重做日志缓冲剩余空间小于 1 / 2 时刷新到重做日志文件;
对于第二种情况,触发写磁盘的条件由 innodb_flush_log_at_trx_commit 参数控制,表示当提交事务时,处理 redo log 的方式。
innodb_flush_log_at_trx_commit 的有效值为 0, 1, 2;默认为 1:
  • 0:提交事务时并不刷新缓冲区,而是等待主线程每秒的刷新;
  • 1:提交事务时将重做日志缓冲区刷新到磁盘;符合事务的 ACID 特性;
  • 2:提交事务时将重做日志缓冲区先刷新到文件系统的缓存中,之后每秒将缓存刷新到磁盘中,也就是说重做日志异步写到磁盘;
当值为 0 和 2 时,数据库发生崩溃,会存在部分日志未刷新到磁盘,因此会丢失部分数据;
MySQL 中的重做日志和二进制日志
文章图片

二进制日志 binlog 前面提到的 redo log 是 InnoDB 引擎特有的日志,而 Server 层也有自己的日志,即二进制日志 binlog,binlog 记录了数据库增删改操作的信息,以二进制的格式保存在磁盘中。当提交事务时,一次性将事务中的所有 SQL 语句写入 binlog 中。
binlog 记录的是逻辑操作,可以简单认为就是 SQL 语句,binlog 是通过追加的方式进行写入的,也就是说当 binlog 文件写到一定大小后切换到下一个,不会覆盖之前的文件。
MySQL 通过 sync_binlog 参数来控制服务层什么时候将 binlog 同步到磁盘上,sync_binlog 的有效值为 0, 1, N,默认为 1:
  • 0:关闭服务层同步 binlog 到磁盘的功能,binlog 的同步依赖于操作系统的不定时同步,该设置提供了最好的性能,但如果操作系统崩溃,就会导致已提交的事务没有同步到磁盘上;
  • 1:每次事务提交都会同步 binlog,该设置是最安全的,但是由于磁盘 IO 频率变高,会影响性能;
  • N:每提交 N 个事务才进行一次 binlog 同步,能提高一定的性能;
作用
  • 主从复制:从库利用主库的 binlog 进行重播,实现主从同步;
  • 恢复:将数据库还原到某一个时间点;
两阶段提交 两阶段提交(two-phase commit,2PC)指的是事务提交分成 prepare 和 commit 两个阶段:
  1. 引擎将更新记录写入 redo log,并将 redo log 同步到磁盘上,此时事务处于 prepared 状态;
  2. 接着执行器生成事务的 binlog,并将 binlog 写入磁盘,然后调用引擎的提交事务接口,告诉引擎提交更改,引擎就会将之前已经准备好的事务(prepared)改成已提交(committed)状态;
经过两个阶段,最终事务成功提交,并且保存在重做日志和二进制日志中。两阶段提交的目的是保证重做日志和二进制日志的一致性,防止数据库恢复时数据不一致。
如果事务处于 prepared 状态时,MySQL 崩溃了,会发生什么?
因为事务此时还未提交,所以根据事务的原子性,这个事务会进行回滚,之前的操作都无效。所以两阶段提交保证了日志的一致性,如果两个日志分开写入,那么就有可能产生不一致的问题。
redo log 和 binlog 的区别
  1. 作用不同:redo log 是 InnoDB 引擎特有的日志,用来保证事务的持久性,是事务层面的;binlog 是 MySQL 服务层实现的,所有引擎都可以使用,起到还原的作用,是数据库层面的;
  2. 内容不同:redo log 是物理日志,记录的是「在某个数据页上做了什么更改」;binlog 是逻辑日志,记录的是语句的原始逻辑,可以简单理解为记录的是 SQL 语句;
  3. 写入方式不同:redo log 是循环写入的;binlog 是追加写入,不会覆盖以前的日志;
  4. 恢复数据的效率不同:基于物理日志的 redo log 恢复数据的效率要比逻辑日志 binlog 高;
参考资料:
  1. MySQL中的重做日志(redo log),回滚日志(undo log),以及二进制日志(binlog)的简单总结
  2. MySQL系列之redo log、undo log和binlog详解
  3. MySQL 45讲
  4. Fun MySQL fact of the day: everything is two-phase

    推荐阅读