InnoDB REDO LOG 详解:从原理到实现(基于 MySQL 8.0)
在现代关系型数据库系统中,事务的 持久性(Durability)是 ACID 特性的关键一环。为了在系统崩溃后仍能恢复数据一致性,InnoDB 引擎引入了 REDO LOG(重做日志)机制。
本文将深入剖析 REDO LOG 的作用、设计思想、组织结构、写入流程以及清理策略,帮助读者全面理解其在 InnoDB 中的核心地位。
🎯 目录
- 为什么需要 REDO LOG?
- REDO LOG 的设计目标与 Physiological Logging
- REDO LOG 的内容分类
- REDO LOG 的三层组织结构
- 高效写入机制(MySQL 8.0 优化)
- REDO LOG 的安全清理:Checkpoint 机制
- 总结
- 延伸阅读
1️⃣ 为什么需要 REDO LOG?
InnoDB 采用 Buffer Pool 缓存数据页以提升性能,对磁盘的修改往往滞后于内存。若此时发生崩溃,未刷盘的脏页将丢失,破坏数据一致性。
🔄 WAL 机制
为解决此问题,InnoDB 引入 WAL(Write-Ahead Logging)机制:
┌─────────────────────────────────────────────────┐
│ 在修改 Page 前,先将变更记录写入 REDO LOG │
│ 并确保 REDO 先于 Page 落盘 │
└─────────────────────────────────────────────────┘
系统重启时,通过重放 REDO LOG,可将 Page 恢复至崩溃前状态,从而保证事务的持久性。
2️⃣ REDO LOG 的设计目标与 Physiological Logging
理想的 REDO LOG 需满足以下要求:
| 设计目标 | 说明 |
|---|---|
| 数据量小 | 减少 I/O 开销,提升吞吐 |
| 幂等性 | 重放多次结果一致,避免重复应用 |
| 基于 Page | 便于并行恢复 |
📊 传统日志对比
| 日志类型 | 优点 | 缺点 |
|---|---|---|
| Logical Logging | 体积小 | 恢复慢 |
| Physical Logging | 恢复快 | 日志量大 |
💡 InnoDB 的解决方案:Physiological Logging
InnoDB 采用 Physiological Logging —— 以 Page 为单位,在 Page 内部使用逻辑方式记录变更。
示例:MLOG_REC_UPDATE_IN_PLACE
(Page ID, Record Offset, (Field 1, Value 1), ..., (Field i, Value i))
这种方式兼顾了日志体积与恢复效率,但也带来两个挑战:
⚠️ 挑战一:Page 状态必须正确
由于 InnoDB Page(默认 16KB)大于文件系统原子写单位(4KB),可能出现"半写页"。
解决方案:InnoDB 引入 Double Write Buffer 机制,确保总能找到一个完整有效的 Page。
⚠️ 挑战二:保证重放幂等
InnoDB 为每条 REDO 分配全局唯一递增的 LSN(Log Sequence Number)。
机制:
- Page 在修改时会记录对应 REDO 的 LSN(存于
FIL_PAGE_LSN字段) - 恢复时,若 Page 的 LSN ≥ REDO 的 LSN,则跳过该 REDO,实现幂等重放
3️⃣ REDO LOG 的内容分类
MySQL 8.0 中定义了 65 种 REDO 类型,可分为三大类:
📁 (1)作用于 Page 的 REDO(占绝大多数)
| 子类型 | 说明 | 示例 |
|---|---|---|
| Index Page | 索引页操作 | MLOG_REC_INSERT, MLOG_REC_UPDATE_IN_PLACE, MLOG_REC_DELETE |
| Undo Page | Undo 页操作 | - |
| R-tree Page | R树页操作 | - |
示例:MLOG_REC_UPDATE_IN_PLACE 结构
┌─────────────────────────────────────────────────────────┐
│ Header (固定) │
├─────────────────────────────────────────────────────────┤
│ Type │ Space ID │ Page Number │
├─────────────────────────────────────────────────────────┤
│ Body (可变) │
├─────────────────────────────────────────────────────────┤
│ Record Offset │ Update Field Count │ (Field Number, │
│ │ │ Data Length, │
│ │ │ Data) × N │
└─────────────────────────────────────────────────────────┘
📂 (2)作用于 Space 的 REDO
用于文件级操作:
| 类型 | 说明 |
|---|---|
MLOG_FILE_CREATE |
文件创建 |
MLOG_FILE_DELETE |
文件删除 |
MLOG_FILE_RENAME |
文件重命名 |
特点:
- 此类 REDO 在操作成功后才记录
- 恢复时主要用于校验文件状态
- Page Number 固定为 0
🏷️ (3)Logic 类型 REDO
不修改数据,仅提供控制信息。
| 类型 | 说明 |
|---|---|
MLOG_MULTI_REC_END |
标识一个原子操作(mtr)的结束 |
4️⃣ REDO LOG 的三层组织结构
REDO LOG 从逻辑到物理分为三层:
📐 (1)逻辑层(Logical REDO)
- 由连续的 REDO 记录组成
- 使用全局递增偏移 sn(sequence number)标识位置
log_sys.sn维护当前最大 sn
📦 (2)物理层(Log Block)
- 磁盘以 Block 为单位读写(512B)
- 每个 Block 结构:
┌──────────────────────────────────────────────────────┐
│ Block Header (12 bytes) │
├──────────────┬──────────────┬──────────────┬─────────┤
│ Flush Flag + │ Data Length │ First Record │ Check- │
│ Block Num │ (2B) │ Offset (2B) │ point │
│ (4B) │ │ │ Num (4B)│
├──────────────────────────────────────────────────────┤
│ Data Area (498 bytes) - Logical REDO │
├──────────────────────────────────────────────────────┤
│ Block Trailer (4 bytes) - Checksum │
└──────────────────────────────────────────────────────┘
跨 Block 存储:由于 REDO 长度不定,可能跨 Block 存储。
🔢 LSN 与 sn 的换算
constexpr inline lsn_t log_translate_sn_to_lsn(lsn_t sn) {
return (sn / LOG_BLOCK_DATA_SIZE * OS_FILE_LOG_BLOCK_SIZE +
sn % LOG_BLOCK_DATA_SIZE + LOG_BLOCK_HDR_SIZE);
}
📄 (3)文件层(Redo Files)
- 文件命名:
ib_logfile0,ib_logfile1, … - 循环使用,由
innodb_log_files_in_group控制数量 - 文件结构:
┌─────────────────────────────────────────────────────┐
│ Block 0: Header Block │
│ - Format (4B) │
│ - Start LSN (8B) │
│ - Creator (32B) │
├─────────────────────────────────────────────────────┤
│ Block 1-3: Checkpoint Blocks (ib_logfile0 only) │
├─────────────────────────────────────────────────────┤
│ Block 4+: Log Data Blocks │
└─────────────────────────────────────────────────────┘
LSN 到文件 offset 的映射:
const auto real_offset = log.current_file_real_offset + (lsn - log.current_file_lsn);
5️⃣ 高效写入机制(MySQL 8.0 优化)
REDO 写入是事务提交的关键路径。MySQL 8.0 引入 无锁并发写入 架构:
🔄 写入流程图
📝 详细步骤
(1)REDO 生成:mtr(mini-transaction)
- 原子操作的所有 REDO 先写入 mtr 的本地
m_log mtr_commit()时批量拷贝到 Log Buffer
(2)写入 Log Buffer:无锁分配
/* Reserve space in sequence of data bytes: */
const sn_t start_sn = log.sn.fetch_add(len);
- 多个 mtr 并发调用
log_buffer_reserve() - 通过
fetch_add原子获取独享空间,避免锁竞争
(3)写入 Page Cache:log_writer + link_buf
link_buf 工作原理:
┌─────────────────────────────────────────────────────────┐
│ link_buf (循环数组) │
│ [LSN % size] → REDO 长度 │
│ │
│ log_writer 扫描找到连续区域 → 调用 pwrite 写入 OS Cache │
└─────────────────────────────────────────────────────────┘
关键变量:
write_lsn:已写入 Page Cache 的位置buf_ready_for_write_lsn:Log Buffer 中连续日志的末尾current_lsn:已分配的最大 LSN
(4)刷盘:log_flusher
log_writer通知log_flusherlog_flusher调用fsync确保持久化
(5)唤醒用户线程
优化策略:
- 条件变量按 Block 分片,减少竞争
- 专用线程负责唤醒:
log_write_notifier/log_flush_notifier
⚙️ 安全级别配置
| 配置值 | 说明 | 安全性 | 性能 |
|---|---|---|---|
innodb_flush_log_at_trx_commit=1 |
REDO 刷盘后提交 | ⭐⭐⭐⭐⭐ | ⭐⭐⭐ |
=2 |
仅写入 OS Cache 即提交 | ⭐⭐⭐ | ⭐⭐⭐⭐⭐ |
6️⃣ REDO LOG 的安全清理:Checkpoint 机制
REDO 文件空间有限,需定期清理已无需保留的日志。InnoDB 通过 Checkpoint 标记可回收位置。
🎯 Checkpoint LSN 的确定
需确保 Checkpoint 之前的 所有脏页均已刷盘:
/* LWM lsn for unflushed dirty pages in Buffer Pool */
const lsn_t lsn = buf_pool_get_oldest_modification_approx();
const lsn_t lag = log.recent_closed.capacity();
lsn_t lwm_lsn = lsn - lag;
/* Note lsn up to which all dirty pages have already been added into Buffer Pool */
const lsn_t dpa_lsn = log_buffer_dirty_pages_added_up_to_lsn(log);
/* Final checkpoint position */
lsn_t checkpoint_lsn = std::min(lwm_lsn, dpa_lsn);
关键概念:
lwm_lsn:Buffer Pool 中最老脏页的 LSNdpa_lsn:已加入 Buffer Pool 的脏页最大 LSNrecent_closed:link_buf,处理脏页注册的乱序问题
💾 Checkpoint 存储
- 位置:
ib_logfile0的 Block 1 和 Block 2(交替写入,防崩溃) - 内容:
| 字段 | 大小 | 说明 |
|---|---|---|
| Checkpoint Number | 8B | 用于判断最新记录 |
| Checkpoint LSN | 8B | Checkpoint 位置 |
| Checkpoint Offset | 8B | 文件偏移 |
| Log Buffer Size | 8B | 暂未使用 |
恢复时:从最新 Checkpoint LSN 开始重放后续 REDO。
7️⃣ 总结
| 特性 | 说明 |
|---|---|
| 核心作用 | 保证事务持久性,支持崩溃恢复 |
| 设计思想 | Physiological Logging(物理+逻辑混合) |
| 关键机制 | LSN 幂等、WAL、Double Write Buffer |
| 性能优化 | 无锁并发写入、link_buf、专用唤醒线程 |
| 空间管理 | Checkpoint + 循环文件 |
REDO LOG 是 InnoDB 实现事务持久性和崩溃恢复的基石。通过 Physiological Logging、LSN 幂等机制、三层存储结构 以及 MySQL 8.0 的无锁并发写入架构,InnoDB 在保证数据安全的同时,极大提升了写入性能。
理解 REDO LOG 的价值:
- ✅ 帮助 DBA 优化配置(如
innodb_log_file_size、innodb_flush_log_at_trx_commit) - ✅ 为开发者设计高性能事务应用提供理论基础
- ✅ 深入理解数据库内核工作原理
8️⃣ 延伸阅读
📚 官方文档与源码
📰 技术文章
📅 本文基于 MySQL 8.0 源码分析
更多推荐

所有评论(0)