凌晨三点,你刚上线一个支付功能。
用户 A 给 B 转了 100 块,页面显示“转账成功”。
可第二天一早,客服炸了——B 的账户没收到钱!
更诡异的是,主库数据是对的,但从库却少了这笔记录。
你懵了:“明明 COMMIT 成功了,怎么还会丢数据?”
如果此时主库故障,高可用进行了切换,此刻你是不是更崩溃?
今天,我们就用一次真实转账,探索一下MySQL里的Redo Log、Binlog以及所谓的两阶段提交(2PC)--这套被无数大厂面试官反复追问、却少有人真正清楚理解的底层逻辑。
1. 你以为的“提交”,其实只是开始
当你执行:
BEGIN;UPDATE accounts SET balance = balance - 100 WHERE user = 'A';UPDATE accounts SET balance = balance + 100 WHERE user = 'B';COMMIT;你以为 COMMIT 一敲,钱就稳了?
错!!! 此时数据可能还在内存里(Buffer Pool),根本没写到磁盘。如果服务器突然断电,这笔交易就会凭空消失。
那 MySQL 怎么保证“不丢”?靠 Redo Log。
Redo Log 是 InnoDB 的“后悔药”:它先把你改的数据“记一笔账”,哪怕机器炸了,重启后也能照着日志把数据补回来。
但问题来了:光有 Redo Log,从库怎么办?
这时候,Binlog 登场了。Binlog 是 MySQL Server 的“操作录像”:它记录你执行了什么 SQL(或具体哪行变了),专门给从库看,也用于备份恢复。
于是,一个事务要同时写两个日志:
InnoDB 写 Redo Log(保自己)
Server 写 Binlog(保别人)
但这两个模块互不隶属,谁先写?谁后写?万一中间崩了怎么办? 这就是灾难的起点。
2. 最危险的“半提交”时刻
想象这个场景:
Redo Log 写成功了(事务在主库已“生效”);
正要写 Binlog,机房跳闸或者服务器故障了!
结果:
主库重启后,这笔转账还在;
但从库压根没收到 Binlog,余额没变。
主从不一致!用户投诉、对账失败、资金差错……
反过来也一样:
Binlog 写进去了,但从库还没拉;
Redo Log 没落盘就崩了 → 主库回滚,但从库执行了 → 凭空多出 100 块!
这两种情况,在金融系统里都是致命事故。
所以,MySQL 必须确保:Redo Log 和 Binlog 要么都成功,要么都失败。
怎么做到?答案就是 —— 两阶段提交(Two-Phase Commit, 2PC)。
3. 两阶段提交:用“准备”锁住不确定性
2PC的核心思想很简单:把提交拆成两步,中间加个“准备”状态,并用事务 ID(XID)把两个日志拴在一起。
如果是默认双1(innodb_flush_log_at_trx_commit、sync_binlog参数都为1,后文也用"双1模式"代指)模式的话,正常流程简化后(写内存等忽略)的长这样:
阶段 | 步骤 | 执行者 | 操作 | 日志状态 | 是否 刷盘 | 作用 |
|---|---|---|---|---|---|---|
| 执行阶段 | 1 | InnoDB + Server | 执行 DML,修改 Buffer Pool;生成 Redo Log(内存)和 Binlog(内存) | Redo: in-memory | 否 | 数据变更暂存内存 |
| Prepare 阶段 | 2 | InnoDB | 将 Redo Log 标记为 | Redo: PREPARE(XID=1001) | 是(fsync) | 锁定事务状态,防止回滚 |
| Commit 阶段 - Step 1 | 3 | Server | 将事务的 Binlog 写入 binlog 文件 | Binlog: contains XID=1001 | 是(fsync) | 确保从库能同步 |
| Commit 阶段 - Step 2 | 4 | InnoDB |
| Redo: COMMIT(XID=1001) | 是(fsync) | 完成事务提交 |
| 完成 | 5 | — | 返回客户端“事务提交成功” | — | — | 对外可见 |
其中每一步的关键动作如下
步骤 | 操作 | 关键动作 |
|---|---|---|
1 | 执行 UPDATE | 数据改在内存,Redo 和 Binlog 先缓存 |
2 | Prepare 阶段 | InnoDB把Redo Log标为PREPARE, 刷盘 |
3 | 写 Binlog | Server 把 Binlog 写入文件,刷盘 |
4 | Commit 阶段 | InnoDB在Redo Log里追加commit,刷盘 |
✅ 只有走完这四步,MySQL才敢告诉客户端:“事务成功”。
注意第 2 步和第 3 步之间的“缝隙”——这是唯一可能出问题的地方。
但正因为有了 PREPARE 状态 + XID,MySQL 才能在崩溃后“断案”。
流程图如下
4. 崩溃恢复:靠 Binlog 当“裁判”
MySQL 启动时,InnoDB 会自动扫描 Redo Log,重点盯住那些卡在 PREPARE 的事务。然后,它做一件事:拿着事务的 XID,去 Binlog 里查有没有这条记录。
Redo Log 状态 | Binlog 中是否存在相同 XID? | 恢复动作 | 原因 |
|---|---|---|---|
PREPARE | ✅ 存在 | 提交(Commit) | Binlog 已落盘,说明事务已进入 Commit 阶段,应完成提交 |
PREPARE | ❌ 不存在 | 回滚(Rollback) | Binlog 未写入,说明事务未完成 2PC,应视为未提交 |
COMMIT | 一定存在(否则是bug) | 已提交,无需处理 | Redo Log 已完整提交 |
无记录 | 无需对比 | 无此事务 | 事务未开始或已清理 |
举个例子:
在Prepare阶段后断电(Redo prepare 成功,Binlog 没写)→ Binlog 查无此 XID → 回滚 → A 和 B 钱都没动 →因为没有binlog,从库也不会有变化,主从一致。
在写Binlog后断电(Binlog 已写,Redo commit没写→Binlog 有记录→提交→转账成功→有binlog,从库也更新成功→主库崩溃恢复后主库操作也成功→ 主从一致。
这就是 2PC 的精妙之处:用Binlog里的状况作为最终裁决者,兜底所有不确定性。
5. 对比:Redo Log vs Binlog
下面从多个维度来对比一下
维度 | Redo Log | Binlog |
|---|---|---|
| 所属层级 | InnoDB 存储引擎层 | MySQL Server 层 |
| 日志类型 | 物理日志(记录“页如何变”) | 逻辑日志(记录“SQL 或行如何变”) |
| 记录内容示例 | “page 123, offset 456: 900 → 800” | UPDATE accounts SET balance=800 WHERE id=1;(STATEMENT) |
| 主要用途 | 崩溃恢复(Crash Recovery) | 主从复制(Replication)、时间点恢复(PITR) |
| 写入时机 | 事务执行过程中生成,提交时刷盘 | 事务提交前生成,提交时写入并可刷盘 |
| 存储方式 | 固定大小的循环文件(默认 48MB,可配置) | 追加写入的多个文件(按 |
| 是否支持事务 | 是(与事务强绑定) | 是(但需配合 InnoDB 才能保证 ACID) |
| 是否跨引擎 | 否(仅 InnoDB 使用) | 是(所有引擎都可写,但 MyISAM 不支持事务) |
| 刷盘控制参数 | innodb_flush_log_at_trx_commit | sync_binlog |
| 是否可关闭 | 可关闭(但强烈不建议,会失去崩溃恢复能力) | 可关闭(但关闭后无法做主从复制或 PITR) |
| 恢复速度 | 极快(直接重做物理页) | 较慢(需解析 SQL 或逐行应用) |
💡Tips:
Redo Log 是 InnoDB 的“内部保险”,Binlog 是 MySQL 的“对外凭证”。两者必须协同,才能既保内(恢复)又保外(复制)
生产环境,这两个参数必须设为1,否则,你所谓的“高可用”,可能只是造成数据不一致的“高风险”项
6. 常见配置组合与风险分析
innodb_flush_log_at_trx_commit | sync_binlog | 安全性 | 性能 | 最大可能丢失数据 | 适用场景 |
|---|---|---|---|---|---|
1 | 1 | 最高 | 低 | 0 事务(操作系统崩溃也不丢失) | 金融、支付等强一致性场景 |
1 | 1000 | 高 | 高 | 最多999个已提交事的Binlog | 高吞吐、容忍短暂主从延迟 |
2 | 1 | 中 | 中 | 操作系统崩溃时可能丢最近1秒 Redo | 一般业务,追求性能及安全性平衡 |
0 | 0 | 低 | 最高 | 最多1秒内的所有事务 | 日志型、非关键数据 |
⚠️ 注意:只要 sync_binlog > 1,就存在主从不一致风险,因为 Binlog 可能未落盘而 Redo Log已提交
7. 高频问题
简单整理几个关于redo log和binlog的高频面试题及回答要点
问题 | 核心要点 | 回答关键词 |
|---|---|---|
Redo Log和Binlog有什么区别 | 层级、内容、用途、刷盘机制 | 物理 vs 逻辑、InnoDB vs Server、崩溃恢复 vs 主从复制 |
为什么需要两阶段提交 | 防止 Redo 与 Binlog 状态不一致 | 原子性、XID 关联、主从一致 |
2PC 流程是怎样的 | Prepare→Binlog→commit | 核心三步、XID、刷盘顺序 |
崩溃后如何恢复 | 查 Redo 中 PREPARE 事务,核对 Binlog | XID 匹配、提交/回滚决策 |
能否只用 Redo Log | 可以恢复,但无法复制 | 缺少Binlog→无法主从同步 |
组提交是什么 | 优化 2PC 的刷盘性能 | 多个事务合并fsync,提升吞吐 |
之前我们整理过其他的日志(slow log、general log、错误日志),对于历史文章链接如下:
MySQL源码学习系列(二)--面试高频问题:general log、slowlog记录顺序
MySQL错误日志文件突然暴涨的原因
慢SQL探秘之为什么我的SQL很慢却没记录在慢查询日志里