WAL 技术(Write-Ahead Logging):预写日志的原理与优势
概述
WAL(Write-Ahead Logging,预写日志)是数据库系统中一项核心设计思想——在将数据写入磁盘之前,先将修改记录写入日志。InnoDB 的 Redo Log 就是 WAL 技术的典型实现。
WAL 的核心原则
先写日志,再写数据。 日志必须先于数据持久化到磁盘。
正确流程:
事务提交 → 写入 Redo Log(顺序 I/O)→ 日志刷盘 → 返回提交成功
↓
(之后)Buffer Pool 脏页刷盘(随机 I/O)
错误流程(没有 WAL):
事务提交 → Buffer Pool 修改 → 直接刷数据盘(随机 I/O)→ 返回提交成功
↑ 性能极差,否则风险极高
为什么 WAL 更快?
1. 顺序 I/O vs 随机 I/O
| 维度 | Redo Log(顺序 I/O) | 数据文件(随机 I/O) |
|---|---|---|
| 写入方式 | 追加写,尾部追加 | 随机写,到处定位 |
| 每秒 I/O 次数 | 数百 MB/s | 数十 MB/s |
| 磁盘寻道 | 几乎不需要 | 每次都需要 |
| 写入效率 | 极高 | 低 |
2. 写入量差异
一次 UPDATE 修改一行数据:
– 修改数据页:需要写入完整的 16KB 数据页
– 写 Redo Log:只需记录几个字节的修改内容
3. 批量合并
WAL 允许将多次对同一数据页的修改合并为一次刷盘:
-- 连续执行 1000 次 UPDATE 同一行
UPDATE users SET balance = balance + 1 WHERE id = 1; -- 写 1000 次 Redo Log
UPDATE users SET balance = balance + 1 WHERE id = 1; -- (每次几个字节)
-- ... 共 1000 次
-- 但数据页只刷一次盘(最终的余额)
WAL 的工作流程
事务执行
│
├── 1. 修改 Buffer Pool 中的数据页(脏页)
│
├── 2. 将修改记录写入 Redo Log Buffer
│
├── 3. 事务提交 → Redo Log 刷盘(WAL 核心步骤)
│
├── 4. 返回客户端"提交成功"
│
└── 5. (后台或 checkpoint 时)脏页刷回磁盘
WAL 的优势
数据安全
- 崩溃后数据不丢:只要 Redo Log 安全落盘,即使数据文件没写回,也能恢复
- 正如其名——”Write-Ahead”,日志走在前面,数据可以慢点来
性能提升
- 提交速度快:只需顺序写少量日志,不等随机写的 I/O
- Group Commit:多个事务的提交可以合并一次刷盘
系统吞吐
- 避免每次提交都触发大量随机 I/O
- Buffer Pool 可以在后台从容地合并脏页写入
-- 一个 UPDATE 在 WAL 下的 I/O 对比
--
-- 没有 WAL:
-- 事务提交 → 立即写 16KB 数据页(随机 I/O)
--
-- 有 WAL:
-- 事务提交 → 写 Redo Log 几十字节(顺序 I/O)
-- 后台 → 合并多次脏页,一次 16KB 写回
--
-- 结果:WAL 下每秒可处理的事务数提升了 10-100 倍
WAL 的代价
| 代价项 | 说明 |
|---|---|
| 额外的磁盘空间 | Redo Log 占用磁盘,通常是数百 MB |
| 恢复时间 | 崩溃后需要重放日志恢复数据 |
| 写放大 | 同样的修改既写日志又写数据,但这是安全换性能 |
| 刷盘策略选择 | innodb_flush_log_at_trx_commit 设置不当会影响安全性 |
面试要点
- WAL 的核心思想:先写日志后写数据,日志先行
- 为什么 WAL 快:日志是顺序 I/O ≈ 500MB/s +,数据写是随机 I/O ≈ 10MB/s
- WAL 不丢数据的前提:日志必须在事务提交时真正刷到磁盘(
innodb_flush_log_at_trx_commit=1) - 并非 MySQL 独有:PostgreSQL 的预写日志(WAL),RocksDB 的 WAL,都基于同一思想
- 一句话总结 WAL:用少量顺序 I/O 代替大量随机 I/O,在保证不丢数据的前提下大幅提升性能
© 版权声明
文章版权归作者所有,未经允许请勿转载。
THE END


暂无评论内容