WAL
No related notes
Outlinks (0)
No outlinks found
Backlinks (0)
No backlinks found
1 · WAL#
S 什么场景 数据库/存储系统在运行时,数据修改先在内存(Buffer Pool)中进行,最终需要持久化到磁盘上的数据页(data page)。
C 什么冲突
- 每次修改都直接写数据页:数据页分散在磁盘各处,产生大量随机 I/O,性能极差
- 只在内存中修改,延迟写盘:一旦进程崩溃或掉电,内存中未落盘的修改全部丢失,无法保证持久性(Durability)
两者矛盾:性能要求延迟写盘,持久性要求立刻写盘。
Q 什么问题 如何同时满足高性能写入和崩溃后数据不丢失?
A 回答问题 Write-Ahead Logging (预写日志):在修改数据页之前,先将变更以日志记录(log record)的形式顺序追加写入日志文件,然后再在合适的时机将脏页刷盘。
1.1 · 核心规则
WAL 协议:一个数据页的修改在被写入磁盘之前,其对应的所有日志记录必须先持久化到日志文件。
即:flush(log) → flush(data page),顺序不可颠倒。
1.2 · 为什么有效
| 问题 | WAL 如何解决 |
|---|---|
| 随机 I/O 慢 | 日志是顺序追加写,速度远高于随机写;脏页可以攒批后再刷盘 |
| 崩溃丢数据 | 日志已落盘,崩溃后通过**重放(redo)**日志即可恢复未落盘的修改 |
| 原子性 | 事务的所有修改记录在日志中,提交时只需保证日志落盘;回滚时通过undo日志撤销 |
1.3 · 日志记录内容
一条典型的日志记录包含:
- LSN (Log Sequence Number):日志的全局递增序号
- 事务 ID
- 操作类型:INSERT / UPDATE / DELETE
- 页面 ID + 偏移量:修改的位置
- Before Image (undo):修改前的数据,用于回滚
- After Image (redo):修改后的数据,用于恢复
1.4 · 崩溃恢复流程 (ARIES 算法)#
崩溃发生
│
▼
1. Analysis (分析阶段)
扫描日志,确定崩溃时的活跃事务和脏页
│
▼
2. Redo (重做阶段)
从最近的 checkpoint 开始,重放所有日志 → 恢复到崩溃前状态
│
▼
3. Undo (撤销阶段)
回滚所有未提交事务的修改
│
▼
恢复完成
1.5 · Checkpoint (检查点)#
日志不断增长,不可能每次恢复都从头重放。Checkpoint 机制:
- 将当前所有脏页刷盘
- 在日志中写入一条 checkpoint 记录
- 恢复时只需从最近的 checkpoint 开始重放
这缩短了恢复时间,也允许截断旧日志释放空间。
1.6 · 典型应用
| 系统 | WAL 实现 |
|---|---|
| PostgreSQL | pg_wal/ 目录下的 WAL 段文件 |
| SQLite | -wal 后缀文件,WAL 模式支持并发读写 |
| MySQL InnoDB | redo log (ib_logfile0/1) + undo log |
| etcd | 基于 WAL 保证 Raft 日志持久性 |
| LevelDB/RocksDB | WAL 保护 MemTable 未落盘数据 |
| Redis AOF | 类似 WAL 但是”先执行再写日志”(post-write logging),崩溃后重放 AOF 恢复数据 |