什么是 WAL
數(shù)據(jù)庫中一種高效的日志算法,對(duì)于非內(nèi)存數(shù)據(jù)庫而言,磁盤I/O操作是數(shù)據(jù)庫效率的一大瓶頸。
在相同的數(shù)據(jù)量下,采用WAL日志的數(shù)據(jù)庫系統(tǒng)在事務(wù)提交時(shí),磁盤寫操作只有傳統(tǒng)的回滾日志的一半左右,大大提高了數(shù)據(jù)庫磁盤I/O操作的效率,從而提高了數(shù)據(jù)庫的性能。
WAL(Write Ahead Log)預(yù)寫日志,是數(shù)據(jù)庫系統(tǒng)中常見的一種手段,用于保證數(shù)據(jù)操作的原子性和持久性。
在計(jì)算機(jī)科學(xué)中,「預(yù)寫式日志」(Write-ahead logging,縮寫 WAL)是關(guān)系數(shù)據(jù)庫系統(tǒng)中用于提供原子性和持久性(ACID 屬性中的兩個(gè))的一系列技術(shù)。
在使用 WAL 的系統(tǒng)中,所有的修改在提交之前都要先寫入 log 文件中。
log 文件中通常包括 redo 和 undo 信息,這樣做的目的可以通過一個(gè)例子來說明。
假設(shè)一個(gè)程序在執(zhí)行某些操作的過程中機(jī)器掉電了,在重新啟動(dòng)時(shí),程序可能需要知道當(dāng)時(shí)執(zhí)行的操作是成功了還是部分成功或者是失敗了。
如果使用了 WAL,程序就可以檢查 log 文件,并對(duì)突然掉電時(shí)計(jì)劃執(zhí)行的操作內(nèi)容跟實(shí)際上執(zhí)行的操作內(nèi)容進(jìn)行比較。在這個(gè)比較的基礎(chǔ)上,程序就可以決定是撤銷已做的操作還是繼續(xù)完成已做的操作,或者是保持原樣。
WAL 允許用 in-place 方式更新數(shù)據(jù)庫。另一種用來實(shí)現(xiàn)原子更新的方法是 shadow paging,它并不是 in-place 方式。用 in-place 方式做更新的主要優(yōu)點(diǎn)是減少索引和塊列表的修改。
ARIES 是 WAL 系列技術(shù)常用的算法。
在文件系統(tǒng)中,WAL 通常稱為 journaling。
PostgreSQL 也是用 WAL 來提供 point-in-time 恢復(fù)和數(shù)據(jù)庫復(fù)制特性。

計(jì)算機(jī)中所有的問題都可以通過添加一個(gè)中間層來解決。
備份
如果想保證對(duì)一個(gè)數(shù)據(jù)的操作可以恢復(fù),可以怎么做?
你不用去想數(shù)據(jù)庫是怎么實(shí)現(xiàn)的,也不用想太高深。其實(shí)這是一個(gè)很簡單的問題,我們常常在處理這種問題。最簡單的方法其實(shí)就是備份一份數(shù)據(jù):當(dāng)我需要對(duì)一條數(shù)據(jù)做更新操作前,先將這條數(shù)據(jù)備份在一個(gè)地方,然后去更新,如果更新失敗,可以從備份數(shù)據(jù)中回寫回來。這樣就可以保證事務(wù)的回滾,就可以保證數(shù)據(jù)操作的原子性了。
其實(shí) SQLite 引入 WAL 之前就是通過這種方式來實(shí)現(xiàn)原子事務(wù),稱之為 rollback journal, rollback journal 機(jī)制的原理是:在修改數(shù)據(jù)庫文件中的數(shù)據(jù)之前,先將修改所在分頁中的數(shù)據(jù)備份在另外一個(gè)地方,然后才將修改寫入到數(shù)據(jù)庫文件中;如果事務(wù)失敗,則將備份數(shù)據(jù)拷貝回來,撤銷修改;如果事務(wù)成功,則刪除備份數(shù)據(jù),提交修改。
WAL
如何做到數(shù)據(jù)的可恢復(fù)(原子性)和提交成功的數(shù)據(jù)被持久化到磁盤(持久性)?
另一種機(jī)制就是WAL,WAL 機(jī)制的原理也很簡單:「修改并不直接寫入到數(shù)據(jù)庫文件中,而是寫入到另外一個(gè)稱為 WAL 的文件中;
如果事務(wù)失敗,WAL 中的記錄會(huì)被忽略,撤銷修改;
如果事務(wù)成功,它將在隨后的某個(gè)時(shí)間被寫回到數(shù)據(jù)庫文件中,提交修改?!?/p>
WAL 的優(yōu)點(diǎn):
- 讀和寫可以完全地并發(fā)執(zhí)行,不會(huì)互相阻塞(但是寫之間仍然不能并發(fā))。
- WAL 在大多數(shù)情況下,擁有更好的性能(因?yàn)闊o需每次寫入時(shí)都要寫兩個(gè)文件)。
- 磁盤 I/O 行為更容易被預(yù)測。
- 使用更少的 fsync()操作,減少系統(tǒng)脆弱的問題。
性能提升
我們都知道,數(shù)據(jù)庫的最大性能挑戰(zhàn)就是磁盤的讀寫,許多人在提供數(shù)據(jù)存儲(chǔ)性能上絞盡腦汁,提出和實(shí)驗(yàn)了一套又一套方法。
其實(shí)所有方案最終總結(jié)出來就三種:「隨機(jī)讀寫改順序讀寫」、「緩沖單條讀寫改批量讀寫」、「單線程讀寫改并發(fā)讀寫」。
WAL 其實(shí)也是這兩種思路的一種實(shí)現(xiàn),一方面 WAL 中記錄事務(wù)的更新內(nèi)容,通過 WAL 將隨機(jī)的臟頁寫入變成順序的日志刷盤,另一方面,WAL 通過 buffer 的方式改單條磁盤刷入為緩沖批量刷盤,再者從 WAL 數(shù)據(jù)到最終數(shù)據(jù)的同步過程中可以采用并發(fā)同步的方式。這樣極大提升數(shù)據(jù)庫寫入性能,因此,WAL 的寫入能力決定了數(shù)據(jù)庫整體性能的上限,尤其是在高并發(fā)時(shí)。
checkpoint
上面講到,使用 WAL 的數(shù)據(jù)庫系統(tǒng)不會(huì)再每新增一條 WAL 日志就將其刷入數(shù)據(jù)庫文件中,一般積累一定的量然后批量寫入,通常使用「頁」為單位,這是磁盤的寫入單位。
同步 WAL 文件和數(shù)據(jù)庫文件的行為被稱為 checkpoint(檢查點(diǎn)),一般在 WAL 文件積累到一定頁數(shù)修改的時(shí)候;
當(dāng)然,有些系統(tǒng)也可以手動(dòng)執(zhí)行 checkpoint。執(zhí)行 checkpoint 之后,WAL 文件可以被清空,這樣可以保證 WAL 文件不會(huì)因?yàn)樘蠖阅芟陆怠?/p>
有些數(shù)據(jù)庫系統(tǒng)讀取請(qǐng)求也可以使用 WAL,通過讀取 WAL 最新日志就可以獲取到數(shù)據(jù)的最新狀態(tài)。
為什么要使用WAL呢?
真正的執(zhí)行操作可能數(shù)據(jù)量會(huì)比較大,操作比較繁瑣,并且寫數(shù)據(jù)不一定是順序?qū)懀匀绻恳淮尾僮鞫家却Y(jié)果flush到可靠存儲(chǔ)(比如磁盤)中才執(zhí)行下一步操作的話,效率就太低了。
換一種思路,如果我們?cè)谧稣嬲牟僮髦?,先將這件事記錄下來,持久化到可靠存儲(chǔ)中(因?yàn)槿罩疽话愫苄?,并且是順序?qū)?,效率很高),然后再去?zhí)行真正的操作。
這樣執(zhí)行真正操作的時(shí)候也就不需要等待執(zhí)行結(jié)果flush到磁盤再執(zhí)行下一步,因?yàn)闊o論在哪一步出錯(cuò),我們都能夠根據(jù)備忘錄重做一遍,得到正確的結(jié)果。
參考
PostgreSQL WAL日志解析與應(yīng)用
https://doc.huodongjia.com/detail-2388.html
你常聽說的WAL到底是什么?
https://www.cnblogs.com/xuwc/p/14037750.html
WAL(預(yù)寫式日志)簡介
https://lessisbetter.site/2020/01/02/wal-introduction
數(shù)據(jù)庫如何使用預(yù)寫式日志(Write Ahead Log, WAL) 解決 IO 問題的?
https://blog.csdn.net/universsky2015/article/details/100528607