一、概述
MySQL 的日志系统是数据库可靠性与一致性的基石。三大日志各司其职,共同保障事务的 ACID 特性:
| 日志类型 | 核心作用 | 保障特性 | 所属层级 |
|---|---|---|---|
| Redo Log | 崩溃恢复、持久化保证 | 持久性 (Durability) | InnoDB 存储引擎层 |
| Undo Log | 事务回滚、多版本并发控制 | 原子性 (Atomicity)、隔离性 (Isolation) | InnoDB 存储引擎层 |
| Binlog | 主从复制、数据归档与恢复 | 一致性 (Consistency) | MySQL Server 层 |
二、Redo Log(重做日志)
2.1 核心原理
Redo Log 是 InnoDB 存储引擎特有的物理日志 ,采用 WAL(Write-Ahead Logging,预写日志) 机制。
WAL 核心思想:先写日志,再写磁盘。当数据修改时,先将修改记录到 Redo Log,事务即可提交成功,数据页可以稍后异步刷盘。
物理日志格式:记录的是"在某个数据页上做了什么修改",例如:
对 表空间X 的数据页Y 的偏移量Z 处写入数据A
2.2 为什么需要 Redo Log?
直接刷盘数据页存在两大问题:
- 性能问题 :数据页大小为 16KB,可能只修改了几字节却要刷整个页,且是随机写(数据页在磁盘上位置分散)
- 可靠性问题:Buffer Pool 基于内存,断电后脏页数据会丢失
Redo Log 的优势:
- 记录内容精简(仅需几十字节:表空间号、数据页号、偏移量、更新值)
- 顺序写性能远高于随机写
- 事务提交时只需保证 Redo Log 持久化,无需等待脏页刷盘
2.3 存储结构与刷盘策略
存储结构:
- 固定大小的循环文件(默认
ib_logfile0和ib_logfile1) - 写满后从头开始覆盖,仅保留未刷盘的脏页日志
刷盘策略 (由 innodb_flush_log_at_trx_commit 控制):
| 参数值 | 行为 | 安全性 | 性能 |
|---|---|---|---|
| 0 | 每秒写入磁盘一次 | 可能丢失 1 秒数据 | 最高 |
| 1 | 每次事务提交都 fsync |
最安全(推荐生产环境) | 较低 |
| 2 | 每次提交写入 OS 缓存,由 OS 决定刷盘时机 | 可能丢失部分数据 | 较高 |
生产环境建议 :设置为 1(双 1 配置之一),配合
sync_binlog=1确保数据安全。
2.4 Crash-Safe 机制
当 MySQL 崩溃重启时,InnoDB 会扫描 Redo Log:
- 已提交事务:重放(前滚)Redo Log,恢复未刷盘的数据修改
- 未提交事务:结合 Undo Log 回滚
这就是 Crash-Safe(崩溃恢复) 能力,确保已提交事务的数据不丢失。
三、Undo Log(回滚日志)
3.1 核心原理
Undo Log 是 逻辑日志 ,记录的是事务修改之前的数据状态,用于实现事务回滚和 MVCC。
逻辑日志格式:记录反向操作
INSERT→ 记录对应的DELETE信息DELETE→ 记录对应的INSERT信息UPDATE→ 记录修改前的旧值
3.2 两大核心作用
1. 事务回滚(保证原子性)
事务执行过程中出现错误或执行 ROLLBACK 时,MySQL 利用 Undo Log 将数据恢复到事务开始前的状态。
执行流程示例:
START TRANSACTION;
DELETE FROM products WHERE id = 10;
-- 发现删错了
ROLLBACK;
- 执行 DELETE 前,先将 id=10 的完整记录写入 Undo Log
- 标记数据行为删除状态
- 执行 ROLLBACK 时,根据 Undo Log 恢复原始数据
2. MVCC(多版本并发控制)
Undo Log 是实现 MVCC 的关键因素之一 :
版本链机制:
-
每行数据包含两个隐藏字段:
-
DB_TRX_ID:创建或最后修改该行的事务 ID -
DB_ROLL_PTR:指向 Undo Log 的回滚指针
-
-
多个事务对同一行数据的修改会形成 Undo Log 版本链
Read View + Undo Log:
-
读已提交(RC):每次 SELECT 创建新的 Read View,读取最新已提交版本
-
可重复读(RR):事务开始时创建 Read View 并复用,通过 Undo Log 版本链找到事务开始前的数据版本
3.3 生命周期与清理机制
Undo Log 管理流程 :
- 事务开始时,分配 Undo Log 空间(位于回滚段 Rollback Segment)
- 事务执行中,记录修改前的数据
- 事务提交后,Undo Log 不会立即删除(需继续为 MVCC 服务)
- Purge 线程定期检查不再被任何事务视图引用的 Undo Log 并回收
监控命令:
SHOW ENGINE INNODB STATUS\G
-- 查看 "History list length",持续增长说明 Purge 跟不上
四、Binlog(二进制日志/归档日志)
4.1 核心原理
Binlog 是 MySQL Server 层实现的逻辑日志,记录所有引起数据变更的操作(DDL、DML),所有存储引擎都可使用。
4.2 主要作用
- 主从复制:主库将 Binlog 传输到从库,从库重放实现数据同步
- 数据恢复:基于时间点的恢复(Point-In-Time Recovery),全量备份 + Binlog 增量恢复
- 数据审计:追踪数据变更历史
4.3 三种记录格式
| 格式 | 记录内容 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|---|
| STATEMENT | 原始 SQL 语句 | 日志量小 | 动态函数(如 NOW()、UUID())可能导致主从不一致 |
简单场景(已不推荐) |
| ROW | 行数据修改前后的值 | 精确复制,无歧义 | 日志量大(批量更新产生大量记录) | 生产环境推荐 |
| MIXED | 自动选择 STATEMENT 或 ROW | 折中方案 | 切换逻辑复杂 | 过渡方案 |
生产建议 :使用
binlog_format=ROW,配合sync_binlog=1。
4.4 与 Redo Log 的关键区别
| 特性 | Redo Log | Binlog |
|---|---|---|
| 层级 | InnoDB 存储引擎层 | MySQL Server 层 |
| 日志类型 | 物理日志(页修改) | 逻辑日志(SQL/行数据) |
| 写入方式 | 循环写(固定大小,覆盖旧日志) | 追加写(文件满后新建,保留历史) |
| 用途 | 崩溃恢复(Crash Recovery) | 主从复制、时间点恢复 |
| 保留内容 | 仅未刷盘的脏页日志 | 全量历史变更记录 |
五、两阶段提交(2PC):保证日志一致性
5.1 为什么需要两阶段提交?
Redo Log 和 Binlog 的写入时机不同:
- Redo Log 在事务执行过程中可不断写入
- Binlog 仅在事务提交时写入
如果直接提交,可能出现两种不一致情况:
情况 1:先写 Redo 再写 Binlog
- Redo 标记提交 → 崩溃 → Binlog 未写入
- 主库数据已更新,但 Binlog 缺失,从库无法同步此事务
情况 2:先写 Binlog 再写 Redo
- Binlog 写入 → 崩溃 → Redo 未标记提交
- 主库回滚此事务,但从库已执行,导致主从不一致
5.2 两阶段提交流程
-- 阶段 1:Prepare
写入 Redo Log,标记为 PREPARE 状态,记录 XID(事务 ID)
-- 阶段 2:Commit
写入 Binlog
更新 Redo Log 为 COMMIT 状态
崩溃恢复判断逻辑 :
- Redo 为 PREPARE 且 Binlog 存在对应 XID → 提交事务
- Redo 为 PREPARE 但 Binlog 无对应 XID → 回滚事务
六、事务执行全流程
以 UPDATE 操作为例,展示三大日志的协作:
┌─────────────────────────────────────────────────────────────┐
│ 1. 开启事务 │
│ └─► 分配 Rollback Segment,准备 Undo Log 空间 │
├─────────────────────────────────────────────────────────────┤
│ 2. 执行 UPDATE │
│ ├─► 记录修改前的旧值到 Undo Log(Buffer Pool 中的 Undo 页)│
│ ├─► 修改 Buffer Pool 中的数据页(标记为脏页) │
│ └─► 将修改记录到 Redo Log Buffer │
├─────────────────────────────────────────────────────────────┤
│ 3. 事务提交(两阶段提交) │
│ ├─► 阶段 1:Redo Log PREPARE(刷盘) │
│ ├─► 写入 Binlog(刷盘) │
│ └─► 阶段 2:Redo Log COMMIT │
├─────────────────────────────────────────────────────────────┤
│ 4. 后台异步刷脏页(Checkpoint) │
│ └─► 脏页刷盘后,对应 Redo Log 空间可被重用 │
├─────────────────────────────────────────────────────────────┤
│ 5. Purge 线程清理 Undo Log │
│ └─► 当无事务需要旧版本数据时,清理过期 Undo Log │
└─────────────────────────────────────────────────────────────┘
七、总结
| 日志 | 核心职责 | 关键机制 | 生产配置建议 |
|---|---|---|---|
| Redo Log | 保证已提交事务不丢失 | WAL、顺序写、循环覆盖 | innodb_flush_log_at_trx_commit=1 |
| Undo Log | 支持回滚和 MVCC | 版本链、Purge 清理 | 监控 History list length,避免膨胀 |
| Binlog | 主从复制与数据恢复 | 两阶段提交、ROW 格式 | binlog_format=ROW,sync_binlog=1 |
三大日志协同保障 ACID:
- 原子性(A):Undo Log 实现回滚
- 一致性(C):Redo + Undo + Binlog 共同保证崩溃后的一致性状态
- 隔离性(I):Undo Log 支持 MVCC 实现非锁定读
- 持久性(D):Redo Log 保证已提交事务不丢失
掌握这三大日志的底层原理,是构建高可用、高性能 MySQL 数据库架构的基础