预写日志 Write Ahead Log 笔记
概念
预写日志 WAL 是数据库系统和分布式系统元数据同步中采用的一种手段,主要用于确保数据操作的原子性和持久性。
数据库系统使用 WAL 时,所有的修改在提交之前都要先写入 log 文件中,这样如果机器在执行某些操作的时候断电了,我们还可以检查 log 文件,然后把记录的内容和实际上执行的操作进行比较。这样数据库就可以决定是撤销操作还是继续完成已做的工作,保证原子性。
对于分布式系统的元数据更新,WAL 是在元数据变更写入到持久化 db 中前,先预写到一个 log 中,然后再由另外的操作将 log apply 到 db 中。这样可以减少每次 db 的写入操作,比实时同步更有效率。
执行流程
以 InnoDB 为例,走一遍 log 数据落盘的流程。
redo log:重做日志,包含内存上的日志缓冲和磁盘上的日志文件两部分。
InnoDB 对数据的更新就是先将更新记录写入 redo log,然后会在系统空闲或者按设定的策略再将日志中的内容更新到磁盘中。这样可以大大减少 IO 操作的频率,同时也实现事务的持久性。
mysql 每执行一条语句,先将记录写入 redo log buffer,记录事务对数据库做了哪些修改。后续某个时间点再一次性将多个额计入写入到 redo log file。当故障发生使内存丢失后,InnoDB 会在重启时重放 redo,将 Page 恢复到崩溃之前的状态。
脏页落盘机制
修改了 buffer 的数据页后导致内存中的数据页和磁盘中的不一致,就出现了脏页。
当进行数据页的修改操作时,首先修改在缓冲池中的页,然后再以一定的频率刷新到磁盘上。刷新到磁盘上的操作不是每次更新时发生,而是使用一种 checkpoint 的机制。
checkpoint 主要解决以下问题:
- 缩短数据库的恢复时间
- 缓冲池不够用的时候,将脏页刷新到磁盘
- 重做日志不可用时,刷新脏页到磁盘
简单来说,checkpoint 就是同步 WAL 文件和数据库文件的行为,发生在 WAL 文件累计到一定页数修改的时候。执行 checkpoint 之后,WAL 文件就可以被清空,保证 WAL 文件不会因为太大而性能下降。
缓冲区数据一般无法直接写入磁盘,需要经过操作系统的 buffer。因此,redo log buffer 实际上是先写入 os cache,然后再用 fsync() 刷到 redo log file。
Redo Log 写入机制
redo log buffer 并不是每次产生内容都立即写入都磁盘进行持久化。因为只有写入完成事务才提交,所以不需要。
redo log 有 redo log buff中、OS cache 中、持久化到磁盘中三种状态。那么问题来了,如果事务还没提交时,会不会有部分 redo log buffer 中的日志被持久化到磁盘?
redo log buffer 的空间占用即将达到限额一半的时候,后台线程会主动将 log buffer 写盘。所以事务还没提交时其中一部分 log 也有可能会被持久化到磁盘。但是问题不大,在恢复阶段 innoDB 会识别到这个问题,不会将其应用到数据库的文件。
优点
- 读写之间可以完全并发执行,不会互相阻塞
- 可以有更好的性能
- 磁盘 IO 行为更容易预测
- 更少使用 fsync() 操作
总结
预写日志 WAL 是数据库系统和分布式系统元数据同步中采用的一种手段,主要用于确保数据操作的原子性和持久性。通过将所有修改在提交之前预先写入 log 文件中,使得即使事务落盘中途断电,还可以通过检查 log 进行比对并恢复。使用 WAL 还可以用于减少持久化写入次数,做到更好的持久化性能。