持久性 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


暂无评论内容