Skip to content
MySQL 变更与运维治理 · 第 2 篇 / 共 4 篇
领域数据与中间件
专题MySQL 专题
当前序列MySQL 变更与运维治理
阅读位置第 2 篇 / 共 4 篇当前专题第 4 个序列 / 共 7 个序列

binlog、redo log、undo log 是怎么配合的

MySQL 的三种日志经常一起出现,但它们分工完全不同:

  • redo log: 保证 InnoDB 崩溃恢复
  • undo log: 支撑回滚和 MVCC
  • binlog: 记录逻辑变更,服务复制和恢复

如果把这三者混在一起,很多线上问题都解释不清,比如:

  • 为什么事务提交成功但机器宕机后数据还在
  • 为什么主从复制能继续同步
  • 为什么长事务会拖慢历史版本回收

先说结论

  • redo log 是物理日志,解决“数据页还没落盘时机器崩了怎么办”
  • undo log 记录旧版本,解决“事务回滚”和“快照读看历史版本”
  • binlog 是逻辑日志,解决“复制、审计、按时间点恢复”
  • 一次事务提交,三者不是重复干同一件事,而是分别覆盖不同恢复面

先把三种日志分清

redo log

redo log 是 InnoDB 引擎层的物理日志。
它记录的是“这个页做了什么修改”。

作用是:

  • 数据页还没来得及刷盘时,先把修改意图顺序写入 redo log
  • 宕机重启后,重放 redo log,把已提交但未来得及落盘的修改补回去

它的核心价值是把“随机写数据页”变成“顺序写日志”,这就是 WAL。

undo log

undo log 保存修改前的旧版本。

作用有两个:

  • 事务回滚时恢复旧值
  • MVCC 读取历史版本时沿链回溯

所以 undo log 既是“回滚日志”,也是“历史版本链”的基础。

binlog

binlog 是 Server 层的逻辑日志。
它记录的是 SQL 或行变更事件,而不是 InnoDB 页修改细节。

主要用途:

  • 主从复制
  • 数据订阅
  • 按时间点恢复
  • 审计

一次 update 到底发生了什么

假设执行:

sql
update account set balance = balance - 100 where id = 1;

从日志视角看,大致会发生这些事:

  1. 读取目标行
  2. 生成该行旧版本,写入 undo log
  3. 修改 Buffer Pool 中的数据页
  4. 记录对应的 redo log
  5. 事务提交时写入 binlog
  6. 通过两阶段提交保证 redo 和 binlog 一致

这里有两个核心点:

  • redo 对应“数据页怎么恢复”
  • binlog 对应“变更事件怎么传播和重放”

为什么需要 redo log

因为数据库不能每次事务提交都立刻把数据页完整刷盘。
那样随机 IO 成本太高。

更现实的做法是:

  • 先把修改写到内存页
  • 同时把 redo 顺序写盘
  • 后续再由后台线程把脏页刷盘

这样即使突然宕机,只要 redo 在,提交过的事务就能恢复回来。

这就是“提交成功不等于数据页已经落盘,但仍然能恢复”的根本原因。

为什么需要 undo log

如果事务执行一半失败,数据库得有办法回到修改前。
undo log 就是这份“后悔药”。

例如:

  1. 事务把余额从 100 改成 80
  2. 后面业务检查失败,事务回滚
  3. MySQL 根据 undo log 把余额恢复成 100

除此之外,MVCC 还会借助 undo log 给旧事务看历史版本。
所以长事务不结束,会拖住 undo 清理。

为什么 redo 和 binlog 都要有

它们不是替代关系,而是分层关系。

redo 不能替代 binlog

  • redo 是 InnoDB 私有格式
  • 只保证本地实例崩溃恢复
  • 不适合给从库复制和外部消费

binlog 不能替代 redo

  • binlog 是逻辑层事件
  • 不保证页级崩溃恢复效率
  • 无法直接承担存储引擎的 WAL 职责

一个管引擎恢复,一个管变更传播。

两阶段提交为什么重要

如果 redo 和 binlog 写入顺序不受控,就可能出现:

  • redo 已提交,binlog 没写成功
  • binlog 写成功,redo 没持久化

这两种都会带来严重问题:

  • 主从数据不一致
  • 崩溃恢复后事务状态不确定

所以 MySQL 提交事务时,会通过两阶段提交把两者绑定起来,常见可理解为:

  1. redo prepare
  2. binlog write and fsync
  3. redo commit

重启恢复时,MySQL 会结合 redo 状态和 binlog 判断事务是否真正提交。

几个线上高频现象怎么解释

1. 长事务导致 undo 堆积

长事务一直不提交,它早期拍下的 Read View 还在,旧版本就不能及时清理。
结果可能是:

  • undo 增长
  • purge 跟不上
  • 查询回旧版本变慢

2. 主从延迟和 binlog 强相关

复制链路本质上就是从库消费主库 binlog。
binlog 格式、事务大小、提交节奏都会影响复制延迟。

3. 提交慢不一定是 SQL 慢

有时 SQL 执行不慢,但事务提交慢,可能卡在:

  • redo 刷盘
  • binlog 刷盘
  • 大事务 binlog 写出

面试和实战都通用的理解方式

你可以把三者记成:

  • undo: 回头看
  • redo: 向前补
  • binlog: 向外发

再进一步就是:

  • undo 负责事务回滚和历史版本
  • redo 负责宕机恢复
  • binlog 负责复制和逻辑恢复

总结

理解三种日志,不是为了背概念,而是为了在看到“事务提交、主从复制、崩溃恢复、长事务堆积”这些现象时,知道它们分别落在哪个层面。

把这三条线分清,MySQL 的很多核心机制就会更立体。

延伸阅读相关文章优先当前专题,再补跨专题关联。
同一序列 · 顺着当前主线继续读大表在线 DDL 怎么做更稳适合把计数、日志、在线变更和归档清理放在一起看。MySQL 专题 · MySQL 变更与运维治理同一序列 · 顺着当前主线继续读冷热数据归档和清理策略怎么设计适合把计数、日志、在线变更和归档清理放在一起看。MySQL 专题 · MySQL 变更与运维治理同专题其他序列 · MySQL 变更治理与数据生命周期大事务为什么会拖垮数据库适合把大事务、DDL、回填、慢 SQL 治理和归档分层放进一条持续治理主线上看。MySQL 专题 · MySQL 变更治理与数据生命周期同专题其他序列 · MySQL 事务、锁与高可用边界分库分表前最容易忽略哪些边界适合把 next-key lock、一致性读、复制延迟和分库分表边界放回同一条一致性主线上判断。MySQL 专题 · MySQL 事务、锁与高可用边界跨专题关联 · 同场景:基础学习@Conditional 系列注解怎么配合使用适合把自动装配、条件装配、Profile 和循环依赖放回 Spring Boot 启动过程里理解。Spring 专题 · Spring Boot 启动、装配与配置跨专题关联 · 同场景:基础学习保留策略和日志压缩适合哪些场景适合把副本机制、acks、批量压缩和日志组织放在一条 Kafka 投递主线上理解。Kafka 专题 · Kafka 投递链路与日志存储机制
继续阅读MySQL 变更与运维治理当前序列第 2 篇 / 共 4 篇当前专题第 4 个序列 / 共 7 个序列
往前看
上一篇COUNT(*) 和 COUNT(列) 到底差在哪回到当前序列上一章上一序列MySQL 存储引擎与执行细节从第 1 篇开始:MVCC 和 Read View 怎么理解
往后看
下一篇大表在线 DDL 怎么做更稳继续当前序列下一章下一序列MySQL 建模与查询执行路径从第 1 篇开始:宽表、范式和反范式怎么取舍

把零散经验整理成可查、可复用、可持续更新的企业级知识门户