預寫式日誌 WAL 簡介
【導讀】WAL 是一種什麼操作?在數據庫和消息隊列中爲什麼用到這種技術?本文對 WAL 做了詳細介紹。
引言
Write Ahead Logging,簡稱 WAL,也被翻譯成預寫式日誌,是數據庫技術中實現事務日誌 (Transaction Journal) 的一種標準方法,可以實現單機事務的原子性,同時可以提高數據庫的寫入效率。
思考如下場景,如何確保原子性:寫操作修改數據庫中 a 和 b 的值,二者是一個事務,需要把 a 和 b 的最新值持久化到磁盤,假如保存完 a 的值,系統宕機了,重新啓動後,a 的值已經寫入,但 b 待寫入的值已經丟失,如何發現事務沒有完成呢?如何保證事務的原子性呢?
可以爲事務加鎖,也爲事務增加標誌位,修改完磁盤數據後,標誌位設置事務爲完成,事務狀態保存在磁盤中,假使保存事務狀態的過程中宕機了,就把事務回滾掉。實現 REDO 和 UNDO,就能實現原子性。
數據庫中針對 Crash 和 Recovery 的解決方案是 WAL。
原理
WAL 的核心思想是先寫日誌再寫數據文件,修改數據文件必須發生在修改操作記錄在日誌文件之後。
本文的日誌指事務的操作日誌,本文提到的日誌都是指事務日誌,不再特殊聲明。
我們看 WAL 怎麼解決宕機和恢復的問題:
-
寫 WAL 前宕機了,重啓後,數據處於事務未執行的狀態。
-
寫 WAL 時宕機了,重啓後,可以檢查到 WAL 數據不正確,回滾當事務前的狀態。
-
寫 WAL 後宕機了,重啓後,把 WAL 中記錄的操作,應用到數據庫文件中,得到事務執行後的狀態。
如此,保證了數據的恢復和事務的原子性。
上面提到的都是寫操作,看一下使用 WAL 時的讀操作。WAL 中可能包含了未寫入到數據庫文件中的最新值,如果讀最新值就需要從 WAL 中讀取,如果 WAL 中未讀到,從數據庫讀到的就是最新的數據。
檢查點:寫入到 WAL 文件中的操作記錄並不一定會立刻應用到數據庫文件上,這個過程是異步的,設計檢查點來記錄已經被應用到數據庫文件上的操作序號,檢查點後面的操作記錄等待被應用到數據庫文件上。
優點
WAL 的作用是解決宕機和恢復的問題,同時也有其他優點:
- 提高寫數據的性能
-
WAL 是順序寫,數據庫文件是隨機寫,順序寫性能高於隨機寫
-
減少寫磁盤次數
-
不直接修改數據庫真實數據
-
合併若干小的事務,一次性 commit 到數據庫
-
保證事務原子性
-
保證事務一致性
-
併發讀寫,比如 SQLite 中,讀寫、讀讀都是可以並行的,比如讀時需要找到 WAL 某個值最後寫入的位置,就可以從該位置讀數據,而寫操作是在 WAL 文件後 Append,二者並行。但寫寫不能並行,因爲 2 次寫操作都要向 WAL 文件 Append 數據,無法同時進行。
-
WAL 文件中記錄了數據的歷史版本,因此可以讀取歷史版本的值,甚至把狀態回滾到某個歷史版本。
缺點
SQLite 提到了 WAL 的幾項缺點:
-
WAL 需要 VFS 的支持。
-
所有使用數據庫的進程必須在同一個機器上,以爲 WAL 是單機的。
-
多讀少寫的場景 WAL 比 rollback-journal 類型要慢 1%~2%。
使用場景
WAL 幾乎是數據存儲 (數據庫只是數據存儲的一個類別,只不過這個類別很大) 的標配:
-
Raft 可以使用 WAL 保存 log Entry 以及狀態
-
數據庫
-
PgSQL 使用 WAL 實現事務日誌實現事務原子性、一致性,提升性能
-
SQLite 使用 WAL 實現原子性
-
MySQL 使用 WAL 實現持久性,保證數據不丟失的情況下提升性能
-
leveldb 也使用 WAL 提升性能,保證操作原子性
轉自:大彬
lessisbetter.site/2020/01/02/wal-introduction/
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/L6Fjpbm3rZXs3X1mrui5eQ