MySQL 的三大Log和两阶段提交

BinLog(二进制日志)

记录所有对数据库的写操作(insert update delete 等),用于主从复制和数据恢复

binlog 采用预写式日志 WAL,即先写日志,再写磁盘

Redo Log (重做日志)

记录事务对数据页的物理修改,用于崩溃恢复;物理日志,记录的是数据页的变化;采用循环写入的方式,大小固定

Undo Log(回滚日志)

记录事务修改前的数据,用于回滚事务和实现 MVCC(多版本并发控制)

两阶段提交(2PC)

保证分布式事务的一致性,确保所有参与者要么全部提交,要么全部回滚。

准备阶段:协调者询问所有的参与者是否可以提交

提交阶段:协调者根据参与者的响应决定提交或回滚

事务提交的过程

准备阶段(Prepare Phase)(第一阶段提交)
  • 事务的修改写入redolog 和 undolog
  • redolog 标记事务为 prepare 阶段
写入 binlog
  • 将事务的修改写入 binlog
  • 如果binlog写入成功,进入提交阶段
提交阶段(Submit Phase)(第二阶段提交)
  • 将 redolog 中的事务标记为 commit 状态
  • 事务的修改正式生效

崩溃恢复的过程

  • 如果 Redo Log 中有 PREPARE 状态的事务,检查 Binlog 是否已写入
  • 如果 Binlog 已写入,则重做(Redo)这些事务
  • 如果 Binlog 未写入,则回滚(Undo)这些事务

为什么需要两阶段提交

假设如果只写一次redolog 的话

  • case1 先写 binlog,再写 redolog:当事务提交后,先写 binlog 成功,但是redolog 在写的时候由于系统断电宕机等,未写入成功;当 MySQL 重启的时候,由于没有 redolog 的记录,所以不会恢复 redolog 的数据;在主从架构中,由于binlog 写入成功,这个事务的数据会同步到从库。因此,此时,从库中的数据会比主库多,造成主从库数据不一致的问题
  • case2 先写 redolog,再写 binlog:当事务提交后,redolog 写成功,但是在写 binlog 的时候数据库宕机。此时,主数据库重启后会从 redolog 恢复数据,但是因为没有 binlog,所以此时恢复的数据不会同步到从库中,从而导致从库中的数据比主库少,造成主从库数据不一致的问题

由上可知,如果只写一次 redolog,无论是先写 binlog 还是 redolog 都会造成主从数据库数据不一致的问题。所以,引出了redolog 被设计为两次提交的模式:第一次写被标记为 prepare 状态,第二次写被标记为 commit 状态

  • redo-log(prepare):在写入准备状态的redo记录时宕机,事务还未提交,不会影响一致性。
  • bin-log:在写bin记录时崩溃,重启后会根据redo记录中的事务ID,回滚前面已写入的数据。
  • redo-log(commit):在bin-log写入成功后,写redo(commit)记录时崩溃,因为bin-log中已经写入成功了,所以从机也可以同步数据,因此重启时直接再次提交事务,写入一条redo(commit)记录即可

两阶段提交的问题

由上可知,两阶段提交的过程中会触发两次刷盘操作,这会成为性能瓶颈

  1. redolog 刷盘
  2. binlog 刷盘

这两次刷盘由以下两个参数控制

  1. sync_binlog = 1 ,表示每次提交事务都会将 binlog cache 里的 binlog 直接持久到磁盘
  2. innodb_flush_log_at_trx_commit = 1 ,表示每次事务提交时,都将缓存在 redo log buffer 里的 redo log 直接持久化到磁盘

使用组提交来优化

组提交(Group Commit) 是数据库系统中一种优化技术,主要用于提高事务提交的性能。它的核心思想是将多个事务的提交操作合并为一个批次,减少磁盘 I/O 操作的次数,从而提升系统的吞吐量。

组提交的工作原理

组提交的核心思想是将多个事务的日志写入操作合并为一个批次。以下是具体的工作流程:

组提交的流程
  1. 事务准备
    • 每个事务在提交前,会将自己的日志写入内存缓冲区。
  2. 组提交触发
    • 当满足一定条件时(如缓冲区满或超时),系统会将缓冲区中的日志批量写入磁盘。
  3. 日志写入
    • 将多个事务的日志一次性写入磁盘。
  4. 事务完成
    • 日志写入完成后,所有事务的提交操作完成。
组提交的条件
  • 缓冲区满
    • 当日志缓冲区达到一定大小时,触发组提交。
  • 超时
    • 如果在一定时间内没有达到缓冲区大小,也会触发组提交。
  • 事务数量
    • 当等待提交的事务数量达到一定阈值时,触发组提交。
组提交的优点
  • 减少磁盘 I/O
    • 将多个事务的日志写入操作合并为一个批次,减少磁盘 I/O 操作的次数。
  • 提高吞吐量
    • 通过批量写入,提高系统的吞吐量,尤其是在高并发场景下。
  • 降低延迟
    • 减少每个事务的提交延迟,提高系统的响应速度。
组提交的实现

组提交的实现依赖于数据库系统的日志管理机制。以下是 MySQL 中组提交的实现方式:

  1. Redo Log 的组提交
  • 在 MySQL 中,Redo Log 的组提交是通过 innodb_flush_log_at_trx_commit 参数控制的。
  • 参数设置
    • innodb_flush_log_at_trx_commit = 1:每次事务提交时,Redo Log 都会写入磁盘(不启用组提交)。
    • innodb_flush_log_at_trx_commit = 2:每次事务提交时,Redo Log 写入操作系统的缓冲区,由操作系统决定何时写入磁盘(部分启用组提交)。
    • innodb_flush_log_at_trx_commit = 0:每秒将 Redo Log 写入磁盘一次(完全启用组提交)。
  1. Binlog 的组提交
  • 在 MySQL 中,Binlog 的组提交是通过 sync_binlog 参数控制的。
  • 参数设置
    • sync_binlog = 1:每次事务提交时,Binlog 都会写入磁盘(不启用组提交)。
    • sync_binlog = N:每 N 个事务提交时,Binlog 批量写入磁盘(启用组提交)。

组提交的示例

以下是一个简单的组提交示例:

  1. 事务 1 提交
    • 将日志写入内存缓冲区。
  2. 事务 2 提交
    • 将日志写入内存缓冲区。
  3. 事务 3 提交
    • 将日志写入内存缓冲区。
  4. 组提交触发
    • 缓冲区满或超时,将事务 1、2、3 的日志一次性写入磁盘。
  5. 事务完成
    • 所有事务的提交操作完成。


MySQL 的三大Log和两阶段提交
https://wuwanhao.github.io/2025/03/07/MySQL的三大Log和2PC/
作者
Wuuu
发布于
2025年3月7日
许可协议