深入详解三大MySQL核心日志:redo log、undo log和bin log
三种日志分别是什么
先给一个全景概览,后面逐一展开:
| 日志 | 所属层 | 核心作用 |
|---|---|---|
| redo log | InnoDB 存储引擎层 | 崩溃恢复,保证事务持久性(Durability) |
| undo log | InnoDB 存储引擎层 | 事务回滚,支持 MVCC |
| binlog | MySQL Server 层 | 主从复制,数据备份与恢复 |
一个关键区别:redo log 和 undo log 是 InnoDB 引擎独有的,binlog 是 MySQL Server 层的功能,和具体存储引擎无关。
打个比方。把 MySQL 想象成一家公司:
- redo log 是工作日志——万一停电了,来电之后根据日志把没做完的工作补上
- undo log 是撤销按钮——发现做错了,可以一步步退回去
- binlog 是档案室——所有操作都有记录,随时可以查阅或复制一份给分公司
redo log:保证崩溃不丢数据
为什么需要redo log
InnoDB 更新数据时,不会每次都直接写磁盘。它会先把数据页从磁盘读到内存(Buffer Pool),在内存中修改,然后由后台线程择机把脏页刷回磁盘。这个机制叫 WAL(Write-Ahead Logging)——先写日志,再写磁盘。
问题来了:如果脏页还没刷回磁盘就崩溃了,修改不就丢了吗?
redo log 解决的就是这个问题。更新操作在修改内存数据页的同时,会把"对某个数据页做了什么修改"记录到 redo log 里。即使崩溃时脏页没刷盘,重启后 InnoDB 会读取 redo log,把未完成的修改重新应用到数据页上。
redo log 记录的是"物理修改"——某个表空间的某个页的某个偏移量,写入了什么数据。 这种日志格式叫物理日志,恢复速度极快,因为它不需要重新执行 SQL 语句,直接把字节级的修改写回去就行。
redo log 的结构
redo log 不是一个无限大的文件,而是固定大小的循环写入结构。它由一组文件组成,比如默认的 ib_logfile0、ib_logfile1。

- write pos:当前写入位置,一边写一边往后推
- checkpoint:已经刷盘的位置,checkpoint 之前的 redo log 对应的数据页已经安全落盘,可以被覆盖
write pos 和 checkpoint 之间的区域是"待处理"的 redo log。如果 write pos 追上了 checkpoint,说明 redo log 满了,此时必须暂停更新,先推进 checkpoint(把脏页刷盘),腾出空间。
这就是为什么 redo log 是固定大小的——它不需要永久保存,数据页一旦安全落盘,对应的 redo log 就可以被覆盖。
undo log:事务回滚与MVCC的基石
两个核心职责
undo log 承担了两个看似不同但紧密相关的职责:
职责一:事务回滚。 事务执行到一半,执行了 UPDATE user SET name = '李四' WHERE id = 1,但还没 COMMIT。此时如果执行 ROLLBACK,InnoDB 需要知道"改之前 name 是什么"——这个信息就存在 undo log 里。
undo log 记录的是逻辑操作的逆操作。如果是 INSERT,undo log 就记一条 DELETE;如果是 UPDATE,undo log 就记旧值。回滚时,执行 undo log 里的逆操作,数据就恢复了。
职责二:支持 MVCC。 这是 undo log 更重要的作用。在之前的 MVCC 文章里我们讲过,InnoDB 的每行记录有隐藏列 DB_TRX_ID(最后修改的事务ID)和 DB_ROLL_PTR(回滚指针)。每次 UPDATE 时,旧版本数据会被写入 undo log,DB_ROLL_PTR 指向它。多次更新后,一条记录就形成了一条版本链。

其他事务做快照读时,沿着版本链找到对自己可见的版本,读不加锁,并发性能直接拉满。
undo log 和 redo log 的区别
| 对比维度 | redo log | undo log |
|---|---|---|
| 记录内容 | 物理修改(页+偏移+数据) | 逻辑逆操作(旧值) |
| 作用 | 崩溃恢复 | 事务回滚 + MVCC |
| 是否可清理 | 数据页刷盘后可覆盖 | 没有事务引用后由 purge 线程清理 |
| 存储位置 | 系统表空间或独立文件 | undo 表空间 |
一个容易混淆的点:undo log 也需要 redo log 的保护。也就是说,写 undo log 的操作本身也会产生 redo log。因为 undo log 也是写在磁盘页上的,崩溃恢复时需要先把 undo log 页恢复好,才能用它来回滚未提交的事务。
示例场景
-- 事务执行:UPDATE users SET balance = 100 WHERE id = 1; -- Undo Log 会记录旧值(如 balance = 50),用于回滚或 MVCC 读取。
binlog:Server层的归档日志
binlog 的作用
binlog 是 MySQL Server 层维护的日志,记录了所有修改数据的 SQL 语句(或行变更)。它的用途和引擎层的日志完全不同:
- 主从复制:主库把 binlog 发送给从库,从库重放 binlog,实现数据同步
- 数据恢复:通过 mysqlbinlog 工具,可以将数据库恢复到某个时间点(Point-in-Time Recovery)
binlog 的三种格式
| 格式 | 记录内容 | 优点 | 缺点 |
|---|---|---|---|
| STATEMENT | SQL 语句原文 | 日志量小 | 某些函数(NOW()、UUID())主从不一致 |
| ROW | 每行数据的变更前后值 | 精确,主从一致 | 日志量大 |
| MIXED | 自动选择 STATEMENT 或 ROW | 折中 | 行为不透明 |
生产环境推荐 ROW 格式。虽然日志量大,但数据一致性有保障,而且 ROW 格式的 binlog 也是基于时间点恢复的最可靠选择。
binlog 和 redo log 的关键区别
| 对比维度 | redo log | binlog |
|---|---|---|
| 所属层 | InnoDB 引擎层 | MySQL Server 层 |
| 记录格式 | 物理日志(页修改) | 逻辑日志(SQL/行变更) |
| 写入方式 | 循环写,空间固定 | 追加写,文件用完切换新文件 |
| 用途 | 崩溃恢复 | 主从复制、数据恢复 |
| 是否所有引擎都有 | 否,InnoDB 独有 | 是,所有引擎通用 |
redo log 是"短命"的——数据安全落盘后就可以被覆盖。binlog 是"长寿"的——它会被永久保留,直到你手动清理。 这也是为什么 binlog 可以用来做时间点恢复,而 redo log 不行。
示例场景
-- 主库执行:INSERT INTO orders VALUES (...); -- Bin Log 记录该操作,从库读取并重放,实现数据同步。
一条UPDATE语句的完整日志流程
现在把三种日志串起来,看一条 UPDATE user SET name = '李四' WHERE id = 1 的完整执行过程:
执行器
│
├── 1. 从磁盘读取 id=1 的数据页到 Buffer Pool(如果不在内存中)
│
├── 2. 写 undo log:记录旧值 name='张三'
│ (用于事务回滚和 MVCC 版本链)
│
├── 3. 更新 Buffer Pool 中的数据页:name 从 '张三' 改为 '李四'
│
├── 4. 写 redo log(prepare 状态)
│ (记录对数据页的物理修改)
│
├── 5. 写 binlog
│ (记录 SQL 语句或行变更)
│
└── 6. 提交事务,redo log 标记为 commit 状态
注意第 4 步和第 6 步:redo log 在事务提交前就写入了,但要经过两个阶段——先 prepare,再 commit。这就是两阶段提交。
两阶段提交:redo log和binlog的协调机制
为什么需要两阶段提交
redo log 和 binlog 是两个独立的日志系统,写入时机不同。如果先写 redo log 再写 binlog,或者反过来,在两者之间崩溃就会导致数据不一致。
场景一:先写 redo log,后写 binlog,中间崩溃
- redo log 已经写入,数据可以恢复
- binlog 没写入,从库没有这条记录
- 主从数据不一致
场景二:先写 binlog,后写 redo log,中间崩溃
- binlog 已经写入,从库会同步这条数据
- redo log 没写入,主库重启后这条修改丢了
- 主从数据不一致
两阶段提交把这两个日志的写入变成一个原子操作。
两阶段提交的流程
事务执行中
│
├── 1. redo log 写入,标记为 prepare 状态
│
├── 2. binlog 写入
│
└── 3. redo log 标记为 commit 状态
崩溃恢复时的判断逻辑:
| 崩溃时机 | redo log 状态 | binlog 是否存在 | 恢复动作 |
|---|---|---|---|
| 步骤 1 之前崩溃 | 无 | 无 | 事务丢失,无需处理 |
| 步骤 1 和 2 之间崩溃 | prepare | 无 | 回滚事务(binlog 不存在,说明事务未完成) |
| 步骤 2 和 3 之间崩溃 | prepare | 有 | 提交事务(binlog 已存在,说明事务已生效) |
| 步骤 3 之后崩溃 | commit | 有 | 正常恢复 |
核心判断依据:redo log 处于 prepare 状态时,检查 binlog 是否完整写入。如果 binlog 完整,就提交;不完整,就回滚。 这样即使中间崩溃,主从数据也保持一致。
三种日志对比
最后把三种日志放在一起做一次完整对比:
| 维度 | redo log | undo log | binlog |
|---|---|---|---|
| 所属层 | InnoDB | InnoDB | Server |
| 记录格式 | 物理日志 | 逻辑日志 | 逻辑日志 |
| 核心作用 | 崩溃恢复(持久性) | 回滚 + MVCC(原子性 + 隔离性) | 复制 + 备份 |
| 写入方式 | 循环写 | 按需写入 undo 表空间 | 追加写 |
| 生命周期 | 数据页刷盘后可覆盖 | 无事务引用后由 purge 清理 | 永久保留(手动清理) |
| 是否可关闭 | 不可 | 不可 | 可以关闭(不推荐) |
对应到事务的 ACID 特性:
- A(原子性):undo log 负责——未提交的事务可以回滚
- C(一致性):由 AID 共同保证
- I(隔离性):undo log + MVCC 负责——读写不阻塞
- D(持久性):redo log 负责——提交后的修改不会因崩溃丢失
小结
MySQL 的三种日志各有分工:redo log 保证崩溃恢复,undo log 支撑事务回滚和 MVCC,binlog 负责主从复制和数据归档。它们通过两阶段提交机制协调工作,确保即使在崩溃场景下,数据也能保持一致。
从设计哲学上看,这种分层日志架构体现了一个基本原则:不同的问题用不同的机制解决,然后用协调机制保证它们的一致性。 redo log 解决"写入性能"问题(WAL 让随机写变顺序写),undo log 解决"并发读写"问题(MVCC 让读写不阻塞),binlog 解决"数据分发"问题(复制和恢复)。三者各司其职,组合在一起,才构成了 MySQL 可靠运行的基石。
到此这篇关于深入详解三大MySQL核心日志:redo log、undo log和bin log的文章就介绍到这了,更多相关MySQL核心日志内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
相关文章
Linux下安装mysql-5.6.12-linux-glibc2.5-x86_64.tar.gz
这篇文章主要介绍了Linux下安装mysql-5.6.12-linux-glibc2.5-x86_64.tar.gz的相关资料,非常不错,具有参考借鉴价值,需要的朋友可以参考下2016-09-09
MySQL提示Truncated incorrect DOUBLE value解决方法
这篇文章给大家介绍了MySQL提示Truncated incorrect DOUBLE value报错的四种解决方法,并通过代码给大家介绍的非常详细,具有一定的参考价值,需要的朋友可以参考下2024-02-02
CentOS 7搭建多实例MySQL8的详细教程(想要几个搞几个)
这篇文章主要介绍了CentOS 7搭建多实例MySQL8的详细教程(想要几个搞几个),本文通过图文并茂的形式给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下2020-05-05


最新评论