2PC+Redo Log、Binlog 和 Undo Log 的写入顺序
1. 为什么需要两阶段提交?
在 MySQL 架构中,InnoDB 存储引擎有自己的 Redo Log(保证持久性、崩溃恢复),而 MySQL Server 层有 Binlog(用于主从复制、时间点恢复)。如果一个事务提交时,这两份日志写入不一致,就会导致:
- 若 Redo Log 已提交但 Binlog 未写入 → 主库恢复后有该数据,从库缺失,主从不一致。
- 若 Binlog 已写入但 Redo Log 未提交 → 从库执行了该事务,主库崩溃恢复后却回滚了,主从不一致。
两阶段提交 就是为了解决 Redo Log 和 Binlog 之间的分布式事务一致性问题,保证它们在逻辑上同时提交或同时回滚。
2. 两阶段提交在 MySQL 中的实现
在 MySQL 中,2PC 的 协调者(Coordinator) 是 Binlog,参与者(Participant) 是 InnoDB 存储引擎。整个过程分为 Prepare 阶段 和 Commit 阶段。
2.1 事务执行阶段(写入 Undo Log 和 Redo Log Prepare)
-
写入 Undo Log
- 执行
UPDATE、DELETE等操作时,InnoDB 先将修改前的数据写入 Undo Log(存储于 Undo 表空间)。 - 作用:支持事务回滚(Rollback)和 MVCC(多版本并发控制)。
- 执行
-
修改内存数据页(Buffer Pool)
- 在 Buffer Pool 中更新数据页,此时数据页变为“脏页”。
-
写入 Redo Log(Prepare 状态)
- 在修改 Buffer Pool 的同时,将修改操作(如 Page 的物理变更)写入 Redo Log 缓冲区。
- 当事务准备提交时,InnoDB 将 Redo Log 刷盘,并将其状态标记为 Prepare。
- Redo Log 记录中包含了该事务的 XID(事务ID),用于后续与 Binlog 匹配。
注意:在事务执行过程中,Undo Log 和 Redo Log(Prepare)的写入是 顺序发生 的,但 Undo Log 的写入是 Redo Log 记录的一部分(InnoDB 对 Undo Log 页的修改也会产生 Redo Log,但这属于内部细节,不改变整体顺序)。
2.2 事务提交阶段(两阶段提交的核心)
当客户端发出 COMMIT 命令时,进入两阶段提交流程:
阶段一:Prepare 阶段
-
步骤 1:InnoDB 将当前事务的 Redo Log 从缓冲区刷入磁盘(如果
innodb_flush_log_at_trx_commit=1),并 将 Redo Log 的状态设置为 Prepare。- 此时 Redo Log 已经持久化,但事务尚未在 Binlog 中记录,因此对外不可见(崩溃恢复时会根据 Binlog 决定提交或回滚)。
-
步骤 2:InnoDB 通知 MySQL Server 层“我准备好了”。
阶段二:Commit 阶段
-
步骤 3:MySQL Server 层将事务的 Binlog 写入磁盘(如果
sync_binlog=1)。- Binlog 记录中同样包含该事务的 XID。
- 这一步是 关键:Binlog 的写入成功与否决定了事务是否最终提交。
-
步骤 4:Binlog 写入成功后,Server 层通知 InnoDB “可以提交了”。
-
步骤 5:InnoDB 将 Redo Log 中该事务的状态从 Prepare 改为 Commit,此时事务在存储引擎层正式提交。
- 这一步通常不需要刷盘,因为 Redo Log 的 Commit 状态只是内存中的一个标记(但为了崩溃恢复,也会记录在 Redo Log 中)。
-
步骤 6:释放锁、清理 Undo Log 等收尾工作。
3. 崩溃恢复时的判断逻辑
如果数据库在事务提交过程中崩溃,重启后 MySQL 会根据 Redo Log 和 Binlog 的状态进行恢复:
- 扫描 Redo Log,找出所有处于 Prepare 状态 的事务(即已经写了 Redo Log 但尚未标记 Commit 的事务)。
- 对于每个 Prepare 事务,根据其 XID 去 Binlog 中查找:
- 如果 Binlog 中存在该 XID 的事务记录 → 说明 Binlog 已经写入成功,此时 提交该事务(将 Redo Log 标记为 Commit)。
- 如果 Binlog 中不存在该 XID 的事务记录 → 说明 Binlog 尚未写入,此时 回滚该事务(利用 Undo Log 撤销修改)。
这保证了:Binlog 和 Redo Log 要么都提交,要么都不提交,实现了主从数据一致性。
4. Undo Log 在其中的角色
- Undo Log 并不是 2PC 的一部分,它在事务执行期间就开始写入,用于支持回滚和 MVCC。
- 如果事务最终回滚(无论是显式
ROLLBACK还是崩溃恢复时回滚),InnoDB 会利用 Undo Log 将数据恢复到修改前的状态。 - 如果事务提交,Undo Log 在事务结束后会被后台线程清理(但清理前仍用于 MVCC)。
5. 流程图(简化版)
事务开始
|
v
执行 UPDATE
|
+---> 写入 Undo Log
|
v
修改 Buffer Pool
|
v
写入 Redo Log (Prepare) <--- 持久化(根据配置)
|
v
用户 COMMIT
|
+---> 阶段1: Redo Log 已 Prepare,准备就绪
|
v
阶段2: 写入 Binlog <--- 持久化(根据配置)
|
+---> 如果 Binlog 写入失败 -> 回滚事务
|
v
InnoDB 将 Redo Log 标记为 Commit
|
v
事务提交完成
6. 总结:2PC 如何保证一致性
| 场景 | 崩溃点 | 恢复后行为 | 结果 |
|---|---|---|---|
| Redo Log Prepare 已刷盘,Binlog 未写入 | Binlog 写入前崩溃 | 在 Binlog 中找不到对应 XID → 回滚事务 | 主库和从库(通过 Binlog)都没有该事务 |
| Redo Log Prepare 已刷盘,Binlog 已写入 | Binlog 写入后、Redo Log Commit 前崩溃 | 在 Binlog 中找到 XID → 提交事务 | 主库重放 Redo Log 提交,从库通过 Binlog 应用,数据一致 |
通过这种 两阶段提交 + XID 匹配 的机制,MySQL 完美地解决了 Redo Log 和 Binlog 之间的数据一致性问题,确保即使在崩溃场景下,主从数据也不会出现分歧。
一句话总结:Undo Log 在事务执行时先写,用于回滚;Redo Log 先进入 Prepare 状态,等待 Binlog 写入成功后转为 Commit,形成两阶段提交,保证两份日志最终一致。
更多推荐



所有评论(0)