Redo Log 的两阶段提交机制详解
概述
两阶段提交(Two-Phase Commit)是 MySQL 保证 Redo Log 与 Binlog 一致性 的核心机制。由于 Redo Log 是 InnoDB 引擎层的日志,而 Binlog 是 Server 层的日志,它们是两个独立的写入通道,必须通过某种协议确保它们记录相同的变更。
为什么需要两阶段提交?
没有两阶段提交,可能出现以下问题:
问题场景
UPDATE users SET balance = 100 WHERE id = 1;
假设事务提交过程中系统崩溃:
场景 A:先写 Redo Log 后写 Binlog(崩溃在中间)
– Redo Log:已写 ✅(事务已持久化)
– Binlog:未写 ❌
– 后果:主库恢复了数据,但从库没有收到 Binlog → 主从不一致
场景 B:先写 Binlog 后写 Redo Log(崩溃在中间)
– Binlog:已写 ✅(从库可能已同步)
– Redo Log:未写 ❌
– 后果:从库多了这条数据,主库崩溃恢复后没有 → 主从不一致
解决方案:两阶段提交
两阶段提交流程
事务提交
│
├── Phase 1:Prepare 阶段
│ ├── 写入 Redo Log(Prepare 状态)
│ └── 刷盘 Redo Log
│
├── Phase 2:Commit 阶段
│ ├── 写入 Binlog
│ ├── 刷盘 Binlog
│ └── 将 Redo Log 标记为 Commit 状态
│
└── 事务完成,返回提交成功
详细时序
-- 执行 UPDATE users SET name = 'new' WHERE id = 1
BEGIN;
-- 1. 在 Buffer Pool 中修改数据
UPDATE users SET name = 'new' WHERE id = 1;
-- 2. Prepare 阶段
-- 写入 Redo Log,状态 = prepare
-- Redo Log 刷盘
-- 此时 Redo Log 内容:
-- [prepare] space=5, page=100, offset=800, data='new'
-- 3. Commit 阶段
-- 写入 Binlog,内容为行变更
-- Binlog 刷盘
-- 此时 Binlog 内容:
-- [Xid: 123] UPDATE users SET name = 'new' WHERE id = 1
-- 将 Redo Log 的 prepare 改为 commit
-- Redo Log 内容变为:
-- [commit] space=5, page=100, offset=800, data='new'
COMMIT;
崩溃恢复时的处理逻辑
系统崩溃重启后,扫描最后一个 Binlog 的位置和 Redo Log 中的 XID:
扫描 Redo Log 中所有 prepare 状态的事务
│
├── 检查该事务的 XID 是否在 Binlog 中
│ │
│ ├── XID 在 Binlog 中 ✅
│ │ → 事务在 prepare 后写入了 Binlog
│ │ → 提交事务(将 Redo Log 标记为 commit)
│ │
│ └── XID 不在 Binlog 中 ❌
│ → 事务还没写到 Binlog 就崩溃了
│ → 回滚事务(使用 Undo Log)
│
└── 恢复完成,数据一致
崩溃场景分析
| 崩溃时机 | Redo Log | Binlog | 恢复行为 | 一致性 |
|---|---|---|---|---|
| 写 Redo Log 之前 | ❌ | ❌ | 数据没变,无需处理 | ✅ |
| Redo Log prepare 后,Binlog 前 | ✅ prepare | ❌ | 回滚事务 | ✅ |
| Binlog 写完后,Redo Log commit 前 | ✅ prepare | ✅ | 提交事务 | ✅ |
| Redo Log commit 后 | ✅ commit | ✅ | 无需处理 | ✅ |
两阶段提交的内部实现
XID(事务 ID)
两阶段提交通过 XID(Transaction ID)连接 Redo Log 和 Binlog:
-- Redo Log 中的记录
REDO: XID=123, prepare, space=5, page=100, offset=800, data='new'
-- Binlog 中的对应记录
# at 123456
# Xid = 123
SET TIMESTAMP=...;
UPDATE users SET name = 'new' WHERE id = 1;
Group Commit
MySQL 5.6+ 实现了 Binlog Group Commit,将多个事务的两阶段提交合并:
事务 T1 Prepare → 事务 T2 Prepare → 事务 T3 Prepare
↓
将三个事务的 Binlog 合并一次刷盘
↓
事务 T1 Commit → T2 Commit → T3 Commit
这样磁盘 I/O 次数从 “2N 次” 降为 “N + 1 次”,大幅提升并发提交性能。
面试要点
- 两阶段提交的目的:保证 Redo Log 和 Binlog 的逻辑一致性
- 两个阶段:Prepare(写 Redo Log)→ Commit(写 Binlog + 标记 Redo Log)
- 崩溃恢复原则:Binlog 中有记录就提交,没有就回滚
- 面试常问:”如果写 Binlog 时崩溃了怎么办?” → 回滚事务
- 性能优化:Group Commit 合并多个事务的提交
- 一句话总结:两阶段提交 = Redo Log Prepare + Binlog 写入 + Redo Log Commit,通过检查 Binlog 是否完整来决定崩溃时是提交还是回滚
© 版权声明
文章版权归作者所有,未经允许请勿转载。
THE END


暂无评论内容