wal全称:write-ahead logging
wal(write-ahead logging,预写日志)是 mysql innodb 存储引擎保证事务持久性的核心机制,核心思想是“先写日志,再写数据”。 具体来说,事务执行时先修改内存中的 buffer pool 数据页(变为脏页),同时生成 redo log 记录物理修改,redo log 先持久化到磁盘后,事务才算提交成功,而数据页可以稍后异步刷盘。这样设计主要是为了性能和崩溃恢复两个目标。
很多人会把它记成一句话: «先写日志,再写数据。» 这句话没错,但很容易产生一个误解: 先把 redo log 写入磁盘 ↓ 然后才能修改
buffer pool 中的数据 实际上并不是这样。
1. wal 真正的先写是什么意思?
假设执行:
update account set balance = 900 where id = 1;
原来的余额:1000
innodb 会先在: buffer pool 中的数据页进行修改: 1000 → 900 同时生成对应的: redo record
于是:
buffer pool 中的数据页
↓
已经修改成 900
↓
成为 dirty page
但是磁盘数据文件中可能仍然是:
1000
真正的 wal 约束发生在持久化阶段:
redo log 先持久化
↓
dirty page 后持久化在脏页写入磁盘数据文件之前,对应的 redo log 必须先完成持久化。
2. 为什么要这样设计?
假设一个事务修改了:
page a page b page c
如果没有 wal,可能发生:
page a 已经刷盘 page b 没刷 page c 没刷 redo 也还没持久化 这时候服务器突然宕机。 内存中的: page b page c 修改就丢了。 而数据库又没有完整的 redo 信息,就很难可靠恢复这些修改。
所以 innodb 要保证:
只要修改后的数据页
有机会写入磁盘
那么恢复它所需要的 redo
必须已经先落盘
这样即使发生:
page a 已刷盘
page b 未刷盘
page c 未刷盘
服务器突然宕机,
重启以后仍然可以:
读取 redo log
↓
crash recovery
↓
重新应用缺失的修改
恢复数据库。3. wal 为什么还能提高性能?
你可能会想:
每次事务提交,直接把修改后的数据页写回磁盘不就行了吗?
问题在于 innodb 的数据页通常是16kb
即使我们只是修改balance
这样一个很小的字段,也可能涉及整个数据页的刷盘。
而且数据页可能分散在磁盘不同位置:
page a
↓
某个位置
page b
↓
另一个位置
page c
↓
又一个位置
容易产生较多随机 i/o。
redo log 则更适合:
顺序追加
所以可以:
修改 buffer pool
↓
生成 redo
↓
redo 先持久化
↓
事务完成
↓
脏页以后再慢慢刷盘
既保证可靠性,又避免每次事务都必须立即把所有数据页刷回磁盘。📌 补充:wal 的性能优化
除了顺序写,innodb 还有组提交(group commit)机制:多个并发事务的 redo log 可以合并成一次 fsync 写入,减少系统调用次数,大幅降低磁盘 iops 消耗。lsn(日志序列号)单调递增,用来标记写入位置,保证重放顺序正确。
到此这篇关于mysql wal 是什么?为什么要先写 redo log 再刷数据页?的文章就介绍到这了,更多相关mysql wal 全解内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!
发表评论