Skip to content

05.1 - Redo Log 详解

定位:Redo Log 是 InnoDB 保证持久性的核心机制——理解 WAL 和物理日志才能理解"MySQL 不怕断电" 面试高频度:⭐⭐⭐⭐⭐ 考查方式:WAL 原理、为什么比写数据快、Redo Log 刷盘策略、崩溃恢复过程


一、这是什么?为什么需要它?

是什么

Redo Log(重做日志)是 InnoDB 的物理日志,记录"对哪个数据页做了什么修改"。它的核心设计是 WAL(Write-Ahead Logging)——先写日志,再写数据。

为什么需要 Redo Log?

如果没有 Redo Log,一个事务提交的完整过程会是:

事务提交 → 立即把修改的数据页写到磁盘 → 确认写入成功

问题是数据页的写入是随机写(16KB 一个页,散落在文件各处),每次提交都写随机位置——性能不可接受。

Redo Log 的解决方案:

事务提交 → 把修改记录以"顺序追加"方式写到 Redo Log → 提交成功 ✅
          (后台线程再慢慢把数据页刷到磁盘)

比如一个转账操作

  • 没有 Redo Log:每次提交都要写 2 个 16KB 的数据页(32KB 随机 IO)
  • 有 Redo Log:每次提交只写几十字节的日志(KB 级顺序 IO)

核心 insight:WAL 的核心思想是"把随机写转成顺序写"——写日志是顺序 IO(快),写数据是随机 IO(慢)。先保证日志安全落盘,数据可以后台异步刷。这样既保证了持久性,又大幅提升了性能。


二、原理拆解

2.1 Redo Log 的物理结构

Redo Log 是固定大小、循环写入的日志文件:

ib_logfile0                      ib_logfile1
┌─────────────────────────┐    ┌─────────────────────────┐
│ 已写入  │ 空闲 │ 已写入  │    │ 已写入  │ 空闲  │已写入  │
└─────────┴──────┴────────┘    └─────────┴──────┴────────┘
          ↑                                  ↑
      write pos (当前写入位置)           checkpoint (已刷盘位置)

                                    write pos 超过 checkpoint 时
                                    → 触发脏页刷盘,推进 checkpoint

三个关键位置:

checkpoint ←──── 已刷盘到数据文件的日志位置

    LSN (Log Sequence Number) → 日志总量(单调递增)

write pos ←─── 当前写入位置(Redo Log 文件中的偏移)

    write pos 不能追上 checkpoint
    → 追上时,强制刷脏页,推进 checkpoint

相关配置:

ini
innodb_log_file_size = 512M     # 每个 Redo Log 文件大小
innodb_log_files_in_group = 2   # Redo Log 文件数量
innodb_log_buffer_size = 16M    # Redo Log Buffer 大小

2.2 写入流程

                       Redo Log 写入流程

  UPDATE users SET age=30 WHERE id=1

  ├── 1. 修改 Buffer Pool 中的数据页(内存)
  │    └── 该页变为"脏页"

  ├── 2. 生成 Redo Log 记录(物理日志)
  │    └── "将 page#42 的 offset#128 处的 age 从 20 改为 30"

  ├── 3. 写入 Redo Log Buffer(内存)
  │    └── 每个 DML 操作都写入 Redo Log Buffer

  ├── 4. Redo Log Buffer → Redo Log 文件(磁盘)
  │    └── 根据 innodb_flush_log_at_trx_commit 配置
  │        ├── 1: 每次事务提交都刷盘(最安全)
  │        ├── 2: 每次提交写 OS cache,每秒刷盘
  │        └── 0: 每秒刷盘(MySQL 进程崩溃可能丢数据)

  └── 5. 后台线程异步刷脏页
       └── Buffer Pool 中的脏页 → 数据文件

2.3 Redo Log 的刷盘策略

innodb_flush_log_at_trx_commit提交时行为安全性性能
1(默认,推荐)每次提交都 fsync Redo Log 到磁盘✅ 不丢数据低(每次提交等待磁盘写)
2每次提交只写到 OS cache,每秒 fsync⚠️ OS 崩溃丢 1s 数据
0不主动写,Master Thread 每秒 fsync⚠️ MySQL 崩溃丢 1s 数据

生产环境推荐innodb_flush_log_at_trx_commit = 1 + sync_binlog = 1

这是最安全配置,但也是性能最低的。如果业务允许(如日志类应用),可以降级为 2。

2.4 组提交(Group Commit)

为什么需要组提交? 多个事务几乎同时提交时,如果每个事务都单独 fsync,磁盘 IO 无法充分利用。

组提交的优化:将多个事务的 Redo Log 刷盘合并为一次 fsync

没有组提交:
  事务 A: fsync ← 等待磁盘
  事务 B: fsync ← 等待磁盘
  事务 C: fsync ← 等待磁盘
  → 3 次 fsync,3 次磁盘 IO 延迟

有组提交:
  事务 A: 准备提交
  事务 B: 准备提交
  事务 C: 准备提交
  ─── 合并 ───
  一次 fsync → 三个事务同时完成
  → 1 次 fsync,1 次磁盘 IO 延迟

  高并发下:每秒可以提交更多事务!

2.5 Checkpoint(检查点)

为什么需要 Checkpoint? Redo Log 是循环写入的,必须确保在覆盖之前,对应的脏页已经刷到磁盘。

Checkpoint 的作用:推进"安全覆盖点"。

没有 Checkpoint:
  Redo Log 写满 → 必须等待所有脏页刷完 → 无法继续写入
  → 数据库"卡住"

有 Checkpoint:
  后台线程持续刷脏页,推进 checkpoint
  只要 checkpoint 跟上 write pos,Redo Log 永远不会满
  → 数据库稳定运行

Checkpoint 触发条件

  • Redo Log 文件使用量超过阈值
  • 脏页比例超过 innodb_max_dirty_pages_pct
  • 自适应刷新算法检测到写入压力

三、图解全景

                    Redo Log 完整流程图


UPDATE 语句


┌─────────────────────┐
│ 1. 修改 Buffer Pool │  ← 内存中修改数据页
│    中的数据页        │
└─────────┬───────────┘
          │ 生成 Redo 记录

┌─────────────────────┐
│ 2. Redo Log Buffer  │  ← 内存日志缓冲
│    ┌───┬───┬───┐   │
│    │A  │B  │C  │   │  ← 多个事务的日志
│    └───┴───┴───┘   │
└─────────┬───────────┘
          │ 组提交、刷盘

┌─────────────────────┐
│ 3. Redo Log 文件     │  ← 磁盘(顺序写)
│    ib_logfile0       │
│    ib_logfile1       │
│    ┌────────────────┐│
│    │A│B│C│...       ││  ← WAL: 先写日志
│    └────────────────┘│
└──────────────────────┘
          │ 事务提交成功 ✅


  ┌───────────────┐
  │ 4. 继续执行     │
  │    用户代码     │  ← 不等待脏页刷盘
  └───────┬───────┘
          │ 后台异步

┌─────────────────────┐
│ 5. 脏页刷入磁盘      │  ← 磁盘(随机写)
│    /data/mysql/     │
│    *.ibd            │
└─────────────────────┘


┌─────────────────────┐
│ 6. 推进 Checkpoint   │  ← 释放 Redo Log 空间
└─────────────────────┘

四、实战验证

查看 Redo Log 配置和状态

sql
-- Redo Log 配置
SHOW VARIABLES LIKE 'innodb_log%';

-- 查看 Redo Log 使用情况
SHOW ENGINE INNODB STATUS\G
-- 查找 LOG 部分:
--
-- LOG
-- ---
-- Log sequence number 26540830           ← 当前 LSN(已写入量)
-- Log flushed up to   26540830           ← 已刷盘 LSN
-- Last checkpoint at  26400000           ← 最近检查点 LSN
-- ... 
-- Log i/o's done: 120.00 writes/sec     ← 写入速度

监控 Redo Log 写入速率

sql
-- 通过 sys 库查看 IO 统计
SELECT * FROM sys.io_global_by_wait_by_bytes
WHERE event_name LIKE '%redolog%';

-- 查看 Redo Log 写入量(MySQL 8.0)
SELECT variable_name, variable_value
FROM performance_schema.global_status
WHERE variable_name LIKE 'Innodb_os_log%';

崩溃恢复模拟思路

sql
-- Redo Log 状态值意义
-- Log sequence number (LSN): 当前写入总量
-- Log flushed up to: 已安全写入磁盘的量
-- Last checkpoint at: 已刷盘的脏页对应的 LSN
--
-- 如果 checkpoint 和 flush 相差很大
-- → 说明大量脏页未刷盘
-- → 如果此时崩溃,恢复时需要用 Redo Log 重放
-- → 恢复时间 = 未刷盘数据量 / 重放速度

-- 查看脏页比例
SHOW GLOBAL STATUS LIKE 'Innodb_buffer_pool_pages_dirty';
SHOW GLOBAL STATUS LIKE 'Innodb_buffer_pool_pages_total';
SELECT 
  (SELECT variable_value FROM performance_schema.global_status
   WHERE variable_name = 'Innodb_buffer_pool_pages_dirty') /
  (SELECT variable_value FROM performance_schema.global_status
   WHERE variable_name = 'Innodb_buffer_pool_pages_total') * 100 
  AS dirty_page_pct;

五、面试视角

追问答案要点
Redo Log 的 WAL 原理?Write-Ahead Logging:先写日志(顺序 IO),再写数据(随机 IO)。保证持久性的同时提升性能
Redo Log 比写数据文件快多少?快 10-100 倍。顺序写 vs 随机写,小日志记录 vs 完整 16KB 数据页
为什么 Redo Log 是物理日志?记录"页+偏移+旧值+新值",恢复时可以直接覆盖数据页,不需要重新执行 SQL
innodb_flush_log_at_trx_commit=1=2 的区别?1=每次提交 fsync 磁盘(不丢数据),2=写 OS cache(OS 崩溃可能丢 1s)
什么是组提交?合并多个事务的 fsync 为一次,高并发下大幅提升 tps
Checkpoint 的作用?推进 Redo Log 可覆盖位置,避免日志写满阻塞,同时限制恢复时间
MySQL 崩溃后恢复过程?从 checkpoint 开始,正向扫描 Redo Log,重放已提交但未刷盘的修改

📚 相关链接

  • Undo Log:**Undo Log** — 配合 Redo Log 完成崩溃恢复
  • Binlog:**Binlog** — Server 层日志
  • 2PC 恢复:**两阶段提交与崩溃恢复**
  • ACID 实现:**事务ACID实现**
  • ← 返回 **日志系统索引**

Knowledge4J — Java 知识库