什么是 WAL
数据库中一种高效的日志算法,对于非内存数据库而言,磁盘I/O操作是数据库效率的一大瓶颈。
在相同的数据量下,采用WAL日志的数据库系统在事务提交时,磁盘写操作只有传统的回滚日志的一半左右,大大提高了数据库磁盘I/O操作的效率,从而提高了数据库的性能。
WAL(Write Ahead Log)预写日志,是数据库系统中常见的一种手段,用于保证数据操作的原子性和持久性。
在计算机科学中,「预写式日志」(Write-ahead logging,缩写 WAL)是关系数据库系统中用于提供原子性和持久性(ACID 属性中的两个)的一系列技术。
在使用 WAL 的系统中,所有的修改在提交之前都要先写入 log 文件中。
log 文件中通常包括 redo 和 undo 信息,这样做的目的可以通过一个例子来说明。
假设一个程序在执行某些操作的过程中机器掉电了,在重新启动时,程序可能需要知道当时执行的操作是成功了还是部分成功或者是失败了。
如果使用了 WAL,程序就可以检查 log 文件,并对突然掉电时计划执行的操作内容跟实际上执行的操作内容进行比较。在这个比较的基础上,程序就可以决定是撤销已做的操作还是继续完成已做的操作,或者是保持原样。
WAL 允许用 in-place 方式更新数据库。另一种用来实现原子更新的方法是 shadow paging,它并不是 in-place 方式。用 in-place 方式做更新的主要优点是减少索引和块列表的修改。
ARIES 是 WAL 系列技术常用的算法。
在文件系统中,WAL 通常称为 journaling。
PostgreSQL 也是用 WAL 来提供 point-in-time 恢复和数据库复制特性。
计算机中所有的问题都可以通过添加一个中间层来解决。
备份
如果想保证对一个数据的操作可以恢复,可以怎么做?
你不用去想数据库是怎么实现的,也不用想太高深。其实这是一个很简单的问题,我们常常在处理这种问题。最简单的方法其实就是备份一份数据:当我需要对一条数据做更新操作前,先将这条数据备份在一个地方,然后去更新,如果更新失败,可以从备份数据中回写回来。这样就可以保证事务的回滚,就可以保证数据操作的原子性了。
其实 SQLite 引入 WAL 之前就是通过这种方式来实现原子事务,称之为 rollback journal, rollback journal 机制的原理是:在修改数据库文件中的数据之前,先将修改所在分页中的数据备份在另外一个地方,然后才将修改写入到数据库文件中;如果事务失败,则将备份数据拷贝回来,撤销修改;如果事务成功,则删除备份数据,提交修改。
WAL
如何做到数据的可恢复(原子性)和提交成功的数据被持久化到磁盘(持久性)?
另一种机制就是WAL,WAL 机制的原理也很简单:「修改并不直接写入到数据库文件中,而是写入到另外一个称为 WAL 的文件中;
如果事务失败,WAL 中的记录会被忽略,撤销修改;
如果事务成功,它将在随后的某个时间被写回到数据库文件中,提交修改。」
WAL 的优点:
- 读和写可以完全地并发执行,不会互相阻塞(但是写之间仍然不能并发)。
- WAL 在大多数情况下,拥有更好的性能(因为无需每次写入时都要写两个文件)。
- 磁盘 I/O 行为更容易被预测。
- 使用更少的 fsync()操作,减少系统脆弱的问题。
性能提升
我们都知道,数据库的最大性能挑战就是磁盘的读写,许多人在提供数据存储性能上绞尽脑汁,提出和实验了一套又一套方法。
其实所有方案最终总结出来就三种:「随机读写改顺序读写」、「缓冲单条读写改批量读写」、「单线程读写改并发读写」。
WAL 其实也是这两种思路的一种实现,一方面 WAL 中记录事务的更新内容,通过 WAL 将随机的脏页写入变成顺序的日志刷盘,另一方面,WAL 通过 buffer 的方式改单条磁盘刷入为缓冲批量刷盘,再者从 WAL 数据到最终数据的同步过程中可以采用并发同步的方式。这样极大提升数据库写入性能,因此,WAL 的写入能力决定了数据库整体性能的上限,尤其是在高并发时。
checkpoint
上面讲到,使用 WAL 的数据库系统不会再每新增一条 WAL 日志就将其刷入数据库文件中,一般积累一定的量然后批量写入,通常使用「页」为单位,这是磁盘的写入单位。
同步 WAL 文件和数据库文件的行为被称为 checkpoint(检查点),一般在 WAL 文件积累到一定页数修改的时候;
当然,有些系统也可以手动执行 checkpoint。执行 checkpoint 之后,WAL 文件可以被清空,这样可以保证 WAL 文件不会因为太大而性能下降。
有些数据库系统读取请求也可以使用 WAL,通过读取 WAL 最新日志就可以获取到数据的最新状态。
为什么要使用WAL呢?
真正的执行操作可能数据量会比较大,操作比较繁琐,并且写数据不一定是顺序写,所以如果每一次操作都要等待结果flush到可靠存储(比如磁盘)中才执行下一步操作的话,效率就太低了。
换一种思路,如果我们在做真正的操作之前,先将这件事记录下来,持久化到可靠存储中(因为日志一般很小,并且是顺序写,效率很高),然后再去执行真正的操作。
这样执行真正操作的时候也就不需要等待执行结果flush到磁盘再执行下一步,因为无论在哪一步出错,我们都能够根据备忘录重做一遍,得到正确的结果。
参考
PostgreSQL WAL日志解析与应用
https://doc.huodongjia.com/detail-2388.html
你常听说的WAL到底是什么?
https://www.cnblogs.com/xuwc/p/14037750.html
WAL(预写式日志)简介
https://lessisbetter.site/2020/01/02/wal-introduction
数据库如何使用预写式日志(Write Ahead Log, WAL) 解决 IO 问题的?
https://blog.csdn.net/universsky2015/article/details/100528607
网友评论