Sqlite 併發讀寫的演進之路

概論

sqlite 底層的存儲基於 B-tree,B-Tree 對底層存儲的基本讀寫單位是頁面,而每個頁面都由全局唯一的頁面編號與之對應,一般來說頁面編號從 1 開始遞增。

類 B-Tree 的存儲引擎修改數據的流程如下圖所示:

從上圖中,需要區分 B-Tree 類的存儲引擎幾個核心的模塊:

最上層的 B-Tree 算法模塊,在進行寫事務的時候,是首先向頁面管理器發起讀頁面到內存中的請求,注意到 B-Tree 模塊並不會直接跟數據庫文件打交道,而是經過頁面管理器模塊(下面會展開說),修改了頁面之後標記爲 “髒頁面”,頁面管理器最終負責將髒頁面落盤到數據庫文件中。

現在來談談 “頁面管理器” 模塊的具體工作,也有的實現稱爲“緩存管理器(buffer manager)”。這個模塊負責:在內存中管理頁面

在內存中管理頁面。這涉及到兩部分內容:

錯誤的恢復,事務的管理、比如:

有了這些基礎的瞭解,我們來看看 sqlite 在併發讀寫方面的演進之路

journal

最早的頁面管理器實現是基於 Journa l 文件的,這個文件存儲頁面在修改之前的內容:

可以看到的是:

WAL

從上面的分析可以看出,以 Journal 文件的機制,每次寫事務:

從 sqlite3.7.0 版本開始(SQLite Release 3.7.0 On 2010-07-21[1]),sqlite 引入了更常見的 WAL 機制來解決頁面的讀寫併發問題,WAL 的原理如下圖所示:

WAL 機制中,事務對頁面的修改:

雖然同一時間仍然只能有一個寫事務在進行,但是讀事務同時存在多個。其核心原因是因爲修改並沒有馬上直接落盤到數據庫文件中,這樣修改的可見性就可以由 wal 索引來控制,即:寫事務儘管寫,讀事務儘管讀,只要控制這些寫事務的修改不在 wal 索引中可見即可。

WAL 雖然支持 “一寫多讀”,而不是 Journal 文件那樣的 “一寫全卡住”,但是還有一個問題沒有解決:在做 checkpoint 操作的時候,連寫事務也不能進行了。

兩個可能的優化方案

以下介紹 sqlite 目前在討論的兩個優化方案,之所以說是 “可能”,因爲看這部分代碼還並沒有合併到主幹中,目前暫時還在分支裏,參見:https://github.com/sqlite/sqlite/tree/begin-concurrent-pnu-wal2。

WAL2:

爲了解決 “checkpoint” 時無法進行寫事務”的痛點,sqlite 目前在嘗試新的 WAL-2 機制。

引入 WAL-2 之後,同時有兩個 WAL 文件,這樣可以:checkpoint 其中一個 WAL 文件時,繼續寫另一個 WAL 文件,下一次再進行 checkpoint 時進行切換,這樣 checkpoint 就不會阻塞住寫操作。

BEGIN CONCURRENT:

目前的 WAL 機制,都只能支持同一時間一個寫事務,BEGIN CONCURRENT 機制可以實現多個寫併發,這篇 SQLite: Begin Concurrent[2] 文檔中,大概描述了一下這個優化的思路:

The key to maximizing concurrency using BEGIN CONCURRENT is to ensure that there are a large number of non-conflicting transactions. In SQLite, each table and each index is stored as a separate b-tree, each of which is distributed over a discrete set of database pages. This means that:

  • • Two transactions that write to different sets of tables never conflict, and that

  • • Two transactions that write to the same tables or indexes only conflict if the values of the keys (either primary keys or indexed rows) are fairly close together.

簡單的理解上面的這段話:

目前這兩個優化,由於還並沒有合併到主幹,所以我也還沒有具體看實現,後續體現在 sqlite 主幹中的存儲引擎方面的優化,再梳理出來。

引用鏈接

[1] SQLite Release 3.7.0 On 2010-07-21:

 https://www.sqlite.org/releaselog/3_7_0.html
[2] SQLite: Begin Concurrent:

 https://www.sqlite.org/cgi/src/doc/begin-concurrent/doc/begin_concurrent.md
[3] sqlite3.36 版本 btree 實現(三)- journal 文件備份機制 - codedump 的網絡日誌:

 https://www.codedump.info/post/20211222-sqlite-btree-3-journal/
[4] sqlite3.36 版本 btree 實現(四)- WAL 的實現 - codedump 的網絡日誌: 

https://www.codedump.info/post/20220106-sqlite-btree-4-wal/

關於 Databend

Databend 是一款開源、彈性、低成本,基於對象存儲也可以做實時分析的新式數倉。期待您的關注,一起探索雲原生數倉解決方案,打造新一代開源 Data Cloud。

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/dhZ-chslkUudyc_uXgVKzQ