持久性 Durability 与 Redo Log

持久性 Durability 与 Redo Log

什么是持久性

持久性保证:一旦事务提交成功,数据的修改永久保存,即使系统崩溃也不会丢失。

START TRANSACTION;
UPDATE accounts SET balance = balance - 100 WHERE id = 1;
UPDATE accounts SET balance = balance + 100 WHERE id = 2;
COMMIT;  -- 提交后,即使机器立刻断电,数据也不会丢失

持久性由 redo log(重做日志) 实现。

为什么需要 redo log

直接写磁盘的问题

如果每次提交事务都直接写磁盘的数据页,性能极差:

方式一:每次提交都写数据页
COMMIT → 找到数据页位置 → 随机写磁盘 → 完成
问题:
1. 数据页位置随机 → 随机 IO,巨慢
2. 每次只改一个小字段也要写整个 16KB 数据页
3. 性能完全不可接受

redo log 的解决方案

方式二:先写 redo log,异步写数据页
COMMIT → 写 redo log(顺序写,极快) → 完成

后续:系统在空闲时,把 redo log 中的操作"重放"到数据页
如果崩溃,恢复时用 redo log 重放

性能优势:
1. redo log 是追加写 → 顺序 IO,快
2. 只需记录修改的字节 → 写少量数据
3. 用户事务提交不用等数据页落盘

Redo Log 的工作流程

                      ┌─────────────┐
                      │  Buffer Pool │
                      │  (内存数据页) │
                      └──────┬──────┘
                             │ flush 到磁盘(异步)
                             ▼
                      ┌─────────────┐
                      │  磁盘数据文件  │
                      └─────────────┘

写入顺序(WAL 技术):

事务执行 SQL
    │
    ├─ 1. 修改 Buffer Pool 中的数据页(内存)
    │
    ├─ 2. 将修改记录写入 redo log buffer(内存)
    │
    ├─ 3. COMMIT 时:redo log buffer 刷入 redo log 文件(磁盘)
    │   └─ 此时事务就算提交成功了
    │
    └─ 4. 后续:后台线程将修改的数据页刷入磁盘数据文件

WAL(Write-Ahead Logging):先写日志,再写数据。这是保证持久性的核心技术。

Redo Log 的物理结构

Redo Log 文件:
┌─────────────────────────────────────────┐
│ redo log file 0 (默认 48MB)              │
├─────────────────────────────────────────┤
│ redo log file 1 (默认 48MB)              │
├─────────────────────────────────────────┤
│ ...                                      │
└─────────────────────────────────────────┘

内部是循环写入的:

写指针 → ① → ② → ③ → ④ → ⑤ → ①(循环)
              ↑
          checkpoint(已落盘的位置,旧日志可以覆盖)

参数配置

-- 查看 redo log 配置
SHOW VARIABLES LIKE 'innodb_log_file_size';     -- 每个 redo 文件大小
SHOW VARIABLES LIKE 'innodb_log_files_in_group'; -- redo 文件数量
SHOW VARIABLES LIKE 'innodb_log_buffer_size';    -- redo log buffer 大小
SHOW VARIABLES LIKE 'innodb_flush_log_at_trx_commit'; -- 刷盘策略

-- MySQL 8.0.30+ 新参数
SHOW VARIABLES LIKE 'innodb_redo_log_capacity';  -- redo log 总容量

刷盘策略(重要)

innodb_flush_log_at_trx_commit 控制 redo log 何时刷入磁盘:

= 0:每秒刷一次
   性能最好,但崩溃可能丢失 1 秒数据

= 1:每次提交都刷(默认)
   性能最差,但最安全(不丢数据)

= 2:每次提交写到操作系统缓存
   比 1 快一点,但操作系统崩溃会丢数据
-- 查看当前策略
SHOW VARIABLES LIKE 'innodb_flush_log_at_trx_commit';

-- 修改
SET GLOBAL innodb_flush_log_at_trx_commit = 1;  -- 最安全
SET GLOBAL innodb_flush_log_at_trx_commit = 2;  -- 性能折中

Crash Recovery(崩溃恢复)

MySQL 崩溃重启后的流程:

1. 检查 redo log 中的 LSN(Log Sequence Number)
2. 对比数据页的 LSN
3. 如果 redo log LSN > 数据页 LSN → 需要恢复
4. 从 checkpoint 位置开始重放 redo log
5. 数据页更新到最新状态
-- 查看恢复状态
SHOW STATUS LIKE 'Innodb_redo_log_checkpoint_lsn';
SHOW STATUS LIKE 'Innodb_redo_log_flushed_to_disk_lsn';
SHOW STATUS LIKE 'Innodb_pages_created';

-- 最后检查
CHECK TABLE user;

Redo Log vs Binlog

两者经常被混淆,但职责完全不同:

对比项 Redo Log Binlog
所属层 InnoDB 存储引擎层 MySQL Server 层
作用 保证持久性、崩溃恢复 主从复制、备份恢复
记录内容 物理日志(页修改) 逻辑日志(SQL 或行变更)
写入时机 事务提交时 事务提交时
循环写入 是(空间复用) 否(追加写入)
两阶段提交 与 binlog 配合:先写 redo log(prepare) → binlog → redo log(commit)

面试要点

  • 持久性的实现:redo log(WAL 技术:先写日志再写数据)
  • WAL 核心理念:日志是顺序写(快),数据是随机写(慢),先保证日志落盘就确认事务成功
  • 刷盘策略innodb_flush_log_at_trx_commit=1 最安全,=2 性能折中,=0 性能最好但有风险
  • 崩溃恢复:重启时用 redo log 重放未写入数据页的修改
  • redo log vs binlog:redo 是 InnoDB 的物理日志(崩溃恢复),binlog 是 Server 层的逻辑日志(主从复制)
  • 两阶段提交:保证 redo log 和 binlog 的一致性

一句话总结:InnoDB 用 redo log 实现持久性——提交事务时先顺序写日志(快),再异步刷数据页(慢),崩溃时通过重放 redo log 恢复到一致状态,这是”先写日志再写数据”的 WAL 技术的核心应用。

© 版权声明
THE END
喜欢就支持一下吧
点赞10 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容