Skip to main content

WAL

📅 2026-04-14 ✏️ 2026-04-14 CS
No related notes

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 机制:

  1. 将当前所有脏页刷盘
  2. 在日志中写入一条 checkpoint 记录
  3. 恢复时只需从最近的 checkpoint 开始重放

这缩短了恢复时间,也允许截断旧日志释放空间。

1.6 · 典型应用

系统WAL 实现
PostgreSQLpg_wal/ 目录下的 WAL 段文件
SQLite-wal 后缀文件,WAL 模式支持并发读写
MySQL InnoDBredo log (ib_logfile0/1) + undo log
etcd基于 WAL 保证 Raft 日志持久性
LevelDB/RocksDBWAL 保护 MemTable 未落盘数据
Redis AOF类似 WAL 但是”先执行再写日志”(post-write logging),崩溃后重放 AOF 恢复数据