Redo Log 的两阶段提交机制详解

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 次”,大幅提升并发提交性能。

面试要点

  1. 两阶段提交的目的:保证 Redo Log 和 Binlog 的逻辑一致性
  2. 两个阶段:Prepare(写 Redo Log)→ Commit(写 Binlog + 标记 Redo Log)
  3. 崩溃恢复原则:Binlog 中有记录就提交,没有就回滚
  4. 面试常问:”如果写 Binlog 时崩溃了怎么办?” → 回滚事务
  5. 性能优化:Group Commit 合并多个事务的提交
  6. 一句话总结:两阶段提交 = Redo Log Prepare + Binlog 写入 + Redo Log Commit,通过检查 Binlog 是否完整来决定崩溃时是提交还是回滚
© 版权声明
THE END
喜欢就支持一下吧
点赞10 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容