在 MySQL(InnoDB)事务执行过程中,Binlog、Redo Log、Undo Log 的写入与刷盘时机紧密配合,共同保障 ACID 特性 和 主从一致性 。它们的协作核心是 两阶段提交(2PC)。
下面从 写入时机、刷盘时机、执行顺序 三个维度详细说明。
一、总体流程:两阶段提交(2PC)
为保证 崩溃恢复一致性 和 主从复制一致性,MySQL 采用如下顺序:

💡 关键点:Redo Log 先 Prepare,Binlog 后写,最后 Redo Log Commit
二、各日志的写入与刷盘时机详解
|--------------|--------------------------------------------------------|----------------------------------------------------------------------------------------------------|----------------------------|
| 日志类型 | 写入时机 | 刷盘时机 | 是否强制落盘? |
| Undo Log | DML 执行时 (修改数据前) → 写入 Undo Page(在 Buffer Pool 中) | 异步刷盘 → 随 Undo Page 脏页由后台线程刷入磁盘 | ❌ 否 (持久性由 Redo Log 保证) |
| Redo Log | DML 执行时 → 写入 Redo Log Buffer(内存) | 在事务 Prepare/Commit 阶段 → 由 innodb_flush_log_at_trx_commit 控制: • =1:每次 COMMIT 强制刷盘 • =0/2:异步刷盘 | ✅ 是(默认) (WAL 机制要求) |
| Binlog | 事务 COMMIT 时 → 先写入 Binlog Cache,再写入 Binlog File(内存) | 在事务 COMMIT 阶段 → 由 sync_binlog 控制: • =1:每次事务刷盘 • =0:由 OS 决定 • =N:每 N 个事务刷一次 | ⚠️ 可配置 (生产建议 =1) |
三、详细步骤分解(以 UPDATE + COMMIT 为例)
步骤 1:执行 DML(如 UPDATE t SET x=1 WHERE id=1)
- InnoDB:
-
- 生成 Undo Log(记录旧值),存入 Undo Page(Buffer Pool)
- 修改聚簇索引页(标记为脏页)
- 生成 Redo Log (物理修改),写入 Redo Log Buffer
此时:Undo 和 Redo 都在内存,未刷盘
步骤 2:执行 COMMIT → 触发两阶段提交
▶ 阶段 1:Prepare 阶段(InnoDB)
- InnoDB 将 Redo Log Buffer 中的日志 刷盘(含事务状态 = PREPARE)
- 返回成功给 Server 层
此时 Redo Log 已持久化(若 innodb_flush_log_at_trx_commit=1**)**
▶ 阶段 2:Commit 阶段(Server 层)
- Server 层将事务的 Binlog 写入 Binlog File(内存)
- 根据
sync_binlog决定是否 刷 Binlog 到磁盘
-
- 若
sync_binlog=1→ 立即fsync()刷盘
- 若
- 然后通知 InnoDB 提交
▶ 阶段 3:InnoDB 最终提交
- InnoDB 写一条 Redo Log(状态 = COMMIT)
- 根据配置决定是否立即刷盘(通常可延迟)
✅ 至此,事务完成
四、崩溃恢复如何保证一致性?
MySQL 通过 Redo Log + Binlog 对账 实现:
|------------------------------------|--------------------|
| 场景 | 恢复动作 |
| 只有 Redo Log(PREPARE),无 Binlog | → 事务未完整提交 → 回滚 |
| Redo Log(PREPARE) + Binlog 都存在 | → 事务已完整提交 → 提交 |
| Redo Log(COMMIT)存在 | → 直接提交 |
💡 这确保了:Binlog 记录的事务 = InnoDB 实际提交的事务,主从不会数据不一致。
五、三者的角色总结
|--------------|--------|-------------|--------------|------------------|
| 日志 | 层级 | 作用 | 刷盘要求 | 生命周期 |
| Undo Log | InnoDB | 回滚 + MVCC | ❌ 异步 | 被 Purge 线程清理 |
| Redo Log | InnoDB | 崩溃恢复 + WAL | ✅ 默认强制 | Checkpoint 后循环覆盖 |
| Binlog | Server | 主从复制 + PITR | ⚠️ 可配置(建议强制) | 手动或自动过期删除 |
六、关键参数配置建议(生产环境)
# Redo Log(安全第一)
innodb_flush_log_at_trx_commit = 1
# Binlog(保证主从一致)
sync_binlog = 1
binlog_format = ROW
# 自动清理
binlog_expire_logs_seconds = 604800 # 7天
⚠️ 如果
innodb_flush_log_at_trx_commit=1但sync_binlog=0,
可能主从不一致(主库崩溃时 Binlog 丢失,但从库可能已收到部分日志)。
七、常见误区澄清
|-----------------------------|----------------------------------------------|
| 误区 | 正确理解 |
| "Binlog 在 DML 时就写" | ❌ 只在 COMMIT 时 写(保证原子性) |
| "Undo Log 需要刷盘才能 COMMIT" | ❌ 不需要,靠 Redo Log 保证其可恢复 |
| "Redo Log 和 Binlog 谁先谁后无所谓" | ❌ 必须 Redo Prepare → Binlog → Redo Commit |
| "关闭 Binlog 能提升性能" | ✅ 能,但失去主从和 PITR 能力 |
总结:三者的写入与刷盘顺序
|---------------------|------------------------|------------------------------------------------|
| 阶段 | 操作 | 日志行为 |
| 1. DML 执行 | UPDATE/INSERT/DELETE | • Undo Log 写入内存 • Redo Log 写入内存(Redo Buffer) |
| 2. COMMIT 开始 | 两阶段提交启动 | --- |
| 3. Prepare | InnoDB 准备提交 | • Redo Log 刷盘(PREPARE) |
| 4. Write Binlog | Server 层记录 | • Binlog 写入文件(内存) • 按 sync_binlog 决定是否刷盘 |
| 5. Commit | InnoDB 最终提交 | • Redo Log 写 COMMIT(可延迟刷盘) |
💡 记住口诀 :
"改数据 → 记 undo → 生 redo → prepare redo → 写 binlog → commit redo"
这种精密协作,使得 MySQL 既能 高性能写入 ,又能 强一致、高可用、可恢复。