binlog、redolog 和 undolog 的区别?
binlog、redolog 和 undolog 的区别?
zhangchangsheng面试考察点
- 日志体系理解:面试官不仅仅是想知道三种日志的名字和定义,更是想知道你是否理解 MySQL 的日志体系架构,以及每种日志在整个事务生命周期中扮演的角色。
- 崩溃恢复机制:考察你是否清楚 MySQL 如何通过 redo log 保证持久性(D)、通过 undo log 保证原子性(A)和实现 MVCC,以及 binlog 在主从复制中的作用。
- 两阶段提交原理:是否理解 redo log 和 binlog 的 “两阶段提交” 机制,为什么需要这个机制来保证数据一致性。
核心答案
MySQL 中这三种日志各司其职,核心区别如下:
| 对比维度 | redo log | undo log | binlog |
|---|---|---|---|
| 归属层级 | InnoDB 引擎层 | InnoDB 引擎层 | MySQL Server 层 |
| 核心作用 | 崩溃恢复,保证持久性 | 事务回滚、MVCC,保证原子性 | 主从复制、数据备份 |
| 写入方式 | 循环写,空间固定 | 按事务写入 | 追加写,文件可配置 |
| 内容形式 | 物理日志(在某某页修改了什么) | 逻辑日志(反向 SQL) | 逻辑日志(SQL 语句或行变更) |
| 事务关联 | 事务提交时刷盘 | 事务执行中持续写 | 事务提交时一次性写入 |
一句话总结:redo log 是 “崩溃恢复的救命稻草”,undo log 是 “后悔药的原料”,binlog 是 “主从复制的信使”。
深度解析
一、三种日志的架构位置
flowchart TB
A["MySQL Server 层<br/>binlog(归档日志)<br/>主从复制:主库写入,从库读取并重放<br/>数据备份:全量备份 + binlog 增量恢复<br/>格式:STATEMENT / ROW / MIXED"] --> B["InnoDB 存储引擎层"]
B --> C["redo log(重做日志)<br/>崩溃恢复 / WAL 机制 / 循环写入"]
B --> D["undo log(回滚日志)<br/>事务回滚 / MVCC 实现 / 读一致性"]
B --> E["磁盘数据文件<br/>ibdata1 / ib_logfile0,1 / *.ibd"]
上图展示了 MySQL 三种日志的架构层级关系:
- binlog:位于 Server 层,所有存储引擎都可以使用。它是 MySQL 的 “归档日志”,主要用于主从复制和数据备份恢复。以事件形式记录所有修改数据的 SQL 语句或行变更。
- redo log 和 undo log:位于 InnoDB 引擎层,是 InnoDB 特有的。redo log 用于崩溃恢复,采用 WAL(Write-Ahead Logging)机制;undo log 用于事务回滚和 MVCC 实现。
- 层级分离的意义:正因为 binlog 在 Server 层,redo log 在引擎层,才需要 “两阶段提交” 来保证两者的一致性。
二、redo log 详解
flowchart TB
F0["ib_logfile0<br/>部分已写入已刷盘,部分为可写入区域"] -->|"写满后切换"| F1["ib_logfile1<br/>可写入区域"]
F1 -->|"写满后再切回,循环写入"| F0
WP["write pos:当前写入位置"] --> GAP["空闲空间 = write pos 到 checkpoint 之间的距离"]
CP["checkpoint:检查点位置"] --> GAP
GAP --> W["当 write pos 追上 checkpoint 时<br/>需暂停写入,先刷脏页,推进 checkpoint"]
上图展示了 redo log 的循环写入机制:
- 循环写入:redo log 不是无限追加的,而是固定大小(默认两个文件各 1GB)循环使用。
write pos记录当前写入位置,checkpoint记录检查点位置。 - WAL 机制:事务提交时,先写 redo log(顺序 I/O),再异步刷脏页到数据文件(随机 I/O)。这样即使数据库崩溃,也可以通过 redo log 恢复未刷盘的数据。
- 刷盘时机:
- 事务提交时(
innodb_flush_log_at_trx_commit=1,最安全) - 后台线程定时刷
- redo log 空间不足时
关键配置:
- 事务提交时(
-- 查看 redo log 配置 |
三、undo log 详解
flowchart TB
subgraph G1["undo log 与事务回滚"]
A["原始数据<br/>id=1, name='张三'"] -->|"执行 UPDATE<br/>SET name='李四'"| B["写入 undo log"]
B -->|"需要回滚"| C["用 undo log<br/>恢复旧值 name='张三'"]
end
subgraph G2["undo log 记录内容"]
D["UPDATE 操作:记录旧值"]
E["DELETE 操作:记录整行数据"]
F["INSERT 操作:记录主键 ID"]
end
subgraph G3["MVCC 读取流程"]
M1["找到当前行数据"] --> M2["检查 trx_id 是否被<br/>其他事务更新过"] --> M3["是:沿 undo log 版本链<br/>找到对当前事务可见的版本"] --> M4["返回该版本数据<br/>实现可重复读"]
end
上图展示了 undo log 的两大核心作用:
- 事务回滚:记录数据的 “反向操作”。
UPDATE记录旧值,DELETE记录整行,INSERT记录主键。当事务回滚时,用 undo log 中的信息恢复数据。 - MVCC 实现:每行数据都有隐藏列
trx_id(事务 ID)和roll_pointer(回滚指针)。通过roll_pointer找到 undo log 中的旧版本,实现 “读已提交” 和 “可重复读” 隔离级别。
undo log 存储位置:
-- MySQL 5.6 之前:存储在共享表空间 ibdata1 |
四、binlog 详解
flowchart LR
subgraph M["主库 Master"]
A["应用层写入"] --> B["binlog<br/>mysql-bin.000001 ..."]
end
B -->|"I/O Thread 拉取"| C["I/O Thread<br/>读取 binlog"]
subgraph S["从库 Slave"]
C --> D["Relay Log<br/>中继日志"] --> E["SQL Thread<br/>重放 SQL"] --> F["数据文件更新"]
end
上图展示了 binlog 在主从复制中的作用:
- 主库写入:事务提交时,将修改操作写入 binlog(追加写入,文件可配置滚动)
- 从库同步:
- I/O Thread:连接主库,读取 binlog,写入本地的 Relay Log(中继日志)
- SQL Thread:读取 Relay Log,重放 SQL 语句,保持与主库数据一致
binlog 三种格式:格式 记录内容 优点 缺点 STATEMENT SQL 语句本身 日志量小 不确定函数可能导致不一致 ROW 行数据变更 数据一致性最好 日志量大 MIXED 混合模式 兼顾两者 复杂场景可能仍有问题
-- 查看 binlog 配置 |
五、两阶段提交
flowchart TB
A["1. 执行 SQL<br/>修改内存数据,写 undo log(回滚用)"] --> B["2. 写 redo log,prepare 状态<br/>【阶段一:准备】"]
B --> C["3. 写 binlog"]
C --> D["4. 写 redo log,commit 状态<br/>【阶段二:提交】"]
D --> E["5. 事务完成"]
F["崩溃恢复:redo log 处于 prepare 状态"] --> G{"binlog 中有对应事务?"}
G -->|"有"| H["提交事务(恢复为 commit)"]
G -->|"无"| I["回滚事务(用 undo log)"]
上图展示了 redo log 和 binlog 的两阶段提交流程:
- 为什么需要两阶段提交:因为 redo log 和 binlog 是两个独立的日志系统,如果不协调,可能出现:
- redo log 写了但 binlog 没写 → 主库恢复后数据丢失,从库没收到
- binlog 写了但 redo log 没写 → 主库恢复后数据回滚,从库却收到了
- 两阶段提交流程:
- Prepare 阶段:写 redo log,标记为
prepare状态 - Commit 阶段:写 binlog,然后写 redo log 标记为
commit状态
- Prepare 阶段:写 redo log,标记为
- 崩溃恢复:如果崩溃时 redo log 处于
prepare状态,通过检查 binlog 是否完整来决定提交还是回滚。
面试高频追问
- 追问一:redo log 为什么采用循环写而不是追加写? 循环写的目的是空间复用。redo log 只需要保留 “从检查点到当前” 的数据,更早的日志对应的数据已经刷盘,可以覆盖。追加写会导致磁盘空间无限增长,不现实。
- 追问二:
innodb_flush_log_at_trx_commit设为 0/1/2 有什么区别?值 行为 安全性 性能 0 每秒刷盘一次 ❌ 可能丢 1 秒数据 最高 1 每次提交都刷盘 ✅ 不丢数据(最安全) 较低 2 每次提交写入 OS 缓存,每秒刷盘 ⚠️ 可能丢 1 秒数据 中等
生产环境强烈推荐设为 1。
3. 追问三:为什么 binlog 不能代替 redo log 做崩溃恢复?
- binlog 是逻辑日志(SQL 或行变更),恢复时需要重新执行,效率低
- redo log 是物理日志(页修改),恢复时直接应用,效率高
- binlog 是追加写,没有记录 “哪些页已刷盘” 的信息,无法精确恢复
4. 追问四:undo log 什么时候会被删除? undo log 不会在事务提交后立即删除,因为可能还有其他事务在用它做 MVCC 读。只有当所有活跃事务都不再需要这个 undo log 时,才会被 purge 线程清理。
常见面试变体
- “MySQL 是如何保证事务的 ACID 特性的?”
- “redo log 和 binlog 有什么区别?为什么要两阶段提交?”
- “MVCC 是怎么实现的?undo log 在其中扮演什么角色?”
- “数据库崩溃后如何恢复数据?”
记忆口诀
三大日志定位:
- redo log:引擎层,循环写,崩溃恢复保持久
- undo log:引擎层,反向记,回滚 MVCC 都靠它
- binlog:Server 层,追加写,主从复制做备份
两阶段提交:先 prepare 后 commit,中间写 binlog,崩溃查状态
总结
redo log、undo log、binlog 是 MySQL 的三大核心日志。redo log 在 InnoDB 引擎层,采用循环写的 WAL 机制,保证崩溃恢复时的持久性;undo log 记录反向操作,支持事务回滚和 MVCC 实现;binlog 在 Server 层,追加写入,用于主从复制和数据备份。三者通过 “两阶段提交” 保证一致性,是 MySQL 高可用架构的基石。
声明:本文转载自小哈的博客 《binlog、redolog 和 undolog 的区别?》,版权归原文作者所有。转载请保留本声明与原文链接。
