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)记录即可
两阶段提交的问题
由上可知,两阶段提交的过程中会触发两次刷盘操作,这会成为性能瓶颈
- redolog 刷盘
- binlog 刷盘
这两次刷盘由以下两个参数控制
- sync_binlog = 1 ,表示每次提交事务都会将 binlog cache 里的 binlog 直接持久到磁盘
- innodb_flush_log_at_trx_commit = 1 ,表示每次事务提交时,都将缓存在 redo log buffer 里的 redo log 直接持久化到磁盘
使用组提交来优化
组提交(Group Commit) 是数据库系统中一种优化技术,主要用于提高事务提交的性能。它的核心思想是将多个事务的提交操作合并为一个批次,减少磁盘 I/O 操作的次数,从而提升系统的吞吐量。
组提交的工作原理
组提交的核心思想是将多个事务的日志写入操作合并为一个批次。以下是具体的工作流程:
组提交的流程
- 事务准备:
- 每个事务在提交前,会将自己的日志写入内存缓冲区。
- 组提交触发:
- 当满足一定条件时(如缓冲区满或超时),系统会将缓冲区中的日志批量写入磁盘。
- 日志写入:
- 将多个事务的日志一次性写入磁盘。
- 事务完成:
- 日志写入完成后,所有事务的提交操作完成。
组提交的条件
- 缓冲区满:
- 当日志缓冲区达到一定大小时,触发组提交。
- 超时:
- 如果在一定时间内没有达到缓冲区大小,也会触发组提交。
- 事务数量:
- 当等待提交的事务数量达到一定阈值时,触发组提交。
组提交的优点
- 减少磁盘 I/O:
- 将多个事务的日志写入操作合并为一个批次,减少磁盘 I/O 操作的次数。
- 提高吞吐量:
- 通过批量写入,提高系统的吞吐量,尤其是在高并发场景下。
- 降低延迟:
- 减少每个事务的提交延迟,提高系统的响应速度。
组提交的实现
组提交的实现依赖于数据库系统的日志管理机制。以下是 MySQL 中组提交的实现方式:
- 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 写入磁盘一次(完全启用组提交)。
- Binlog 的组提交
- 在 MySQL 中,Binlog 的组提交是通过
sync_binlog
参数控制的。 - 参数设置:
sync_binlog = 1
:每次事务提交时,Binlog 都会写入磁盘(不启用组提交)。sync_binlog = N
:每 N 个事务提交时,Binlog 批量写入磁盘(启用组提交)。
组提交的示例
以下是一个简单的组提交示例:
- 事务 1 提交:
- 将日志写入内存缓冲区。
- 事务 2 提交:
- 将日志写入内存缓冲区。
- 事务 3 提交:
- 将日志写入内存缓冲区。
- 组提交触发:
- 缓冲区满或超时,将事务 1、2、3 的日志一次性写入磁盘。
- 事务完成:
- 所有事务的提交操作完成。
MySQL 的三大Log和两阶段提交
https://wuwanhao.github.io/2025/03/07/MySQL的三大Log和2PC/