最美情侣中文字幕电影,在线麻豆精品传媒,在线网站高清黄,久久黄色视频

歡迎光臨散文網(wǎng) 會(huì)員登陸 & 注冊(cè)

Sqlite 并發(fā)讀寫的演進(jìn)之路

2022-09-08 16:43 作者:Databend  | 我要投稿

概論

sqlite 底層的存儲(chǔ)基于 B-tree,B-Tree 對(duì)底層存儲(chǔ)的基本讀寫單位是頁(yè)面,而每個(gè)頁(yè)面都由全局唯一的頁(yè)面編號(hào)與之對(duì)應(yīng),一般來(lái)說(shuō)頁(yè)面編號(hào)從 1 開始遞增。類 B-Tree 的存儲(chǔ)引擎修改數(shù)據(jù)的流程如下圖所示:

從上圖中,需要區(qū)分 B-Tree 類的存儲(chǔ)引擎幾個(gè)核心的模塊:

  • B-Tree 算法模塊:從頁(yè)面管理器中讀取頁(yè)面到內(nèi)存,進(jìn)行邏輯的修改,修改完畢之后標(biāo)記該頁(yè)面為臟頁(yè)面,這樣頁(yè)面管理器就知道哪些頁(yè)面被修改,后續(xù)需要進(jìn)行落盤。

  • 頁(yè)面管理器:負(fù)責(zé)向 B-Tree 算法模塊提供根據(jù)頁(yè)面編號(hào)讀、寫頁(yè)面的接口。

  • 數(shù)據(jù)庫(kù)文件:這其實(shí)不是一個(gè)模塊,泛指在磁盤上的數(shù)據(jù)庫(kù)相關(guān)文件,任何的修改最終都要落到數(shù)據(jù)庫(kù)文件。在 sqlite 中,數(shù)據(jù)庫(kù)文件是單一文件,在其他存儲(chǔ)引擎里可能是一組相關(guān)的文件。

最上層的 B-Tree 算法模塊,在進(jìn)行寫事務(wù)的時(shí)候,是首先向頁(yè)面管理器發(fā)起讀頁(yè)面到內(nèi)存中的請(qǐng)求,注意到 B-Tree 模塊并不會(huì)直接跟數(shù)據(jù)庫(kù)文件打交道,而是經(jīng)過(guò)頁(yè)面管理器模塊(下面會(huì)展開說(shuō)),修改了頁(yè)面之后標(biāo)記為“臟頁(yè)面”,頁(yè)面管理器最終負(fù)責(zé)將臟頁(yè)面落盤到數(shù)據(jù)庫(kù)文件中。

現(xiàn)在來(lái)談?wù)劇绊?yè)面管理器”模塊的具體工作,也有的實(shí)現(xiàn)稱為“緩存管理器(buffer manager)”。這個(gè)模塊負(fù)責(zé):在內(nèi)存中管理頁(yè)面

在內(nèi)存中管理頁(yè)面。這涉及到兩部分內(nèi)容:

  • 如果頁(yè)面當(dāng)前不在內(nèi)存中,需要根據(jù)頁(yè)面編號(hào)到磁盤上加載頁(yè)面。

  • 頁(yè)面也并不是每一次讀寫時(shí)都要到磁盤上加載,有些時(shí)候頁(yè)面已經(jīng)在緩存中存在了,這種情況下不需要到磁盤上加載頁(yè)面數(shù)據(jù)。于是,“頁(yè)面管理器”模塊還需要負(fù)責(zé)維護(hù)這些內(nèi)存中的頁(yè)面緩存,何時(shí)淘汰這些頁(yè)面、淘汰哪些內(nèi)存中的頁(yè)面、何時(shí)真正從磁盤上加載,都是這個(gè)模塊的工作。

  • 對(duì)外部而言(這里的外部更多的是 B-Tree 算法模塊),其實(shí)不需要也看不到頁(yè)面緩存的細(xì)節(jié),頁(yè)面管理器對(duì)外提供根據(jù)頁(yè)面編號(hào)讀、寫頁(yè)面接口即可。

錯(cuò)誤的恢復(fù),事務(wù)的管理、比如:

  • 一次事務(wù)要修改 N 個(gè)頁(yè)面,修改到中間的時(shí)候,進(jìn)程崩潰了,這時(shí)候重新啟動(dòng)時(shí)需要恢復(fù)到這個(gè)事務(wù)之前的數(shù)據(jù)成功啟動(dòng),即需要提供回滾事務(wù)的功能。

  • 同樣的一個(gè)事務(wù)要修改 N 個(gè)頁(yè)面,在事務(wù)還未提交的時(shí)候,如果事務(wù)級(jí)別不是 read uncommitted, 那么前面的修改效果不能被其他事務(wù)可見,這也是頁(yè)面管理器需要做的事情,畢竟它對(duì)外提供了讀、寫頁(yè)面的接口,同一個(gè)頁(yè)面編號(hào)的頁(yè)面什么時(shí)候的內(nèi)容可見都由它來(lái)決定。

有了這些基礎(chǔ)的了解,我們來(lái)看看?sqlite 在并發(fā)讀寫方面的演進(jìn)之路

journal

最早的頁(yè)面管理器實(shí)現(xiàn)是基于 Journal 文件的,這個(gè)文件存儲(chǔ)頁(yè)面在修改之前的內(nèi)容:

可以看到的是:

  • Journal 文件存儲(chǔ)了一個(gè)事務(wù)所要修改的頁(yè)面在修改之前的內(nèi)容,這個(gè)定義有點(diǎn)拗口,姑且稱為“舊頁(yè)面內(nèi)容”。

  • 每次一個(gè)事務(wù)提交之后,意味著這個(gè)事務(wù)所有隊(duì)頁(yè)面的修改都已經(jīng)落到了數(shù)據(jù)庫(kù)文件中,這時(shí)候 Journal 文件里保存的舊頁(yè)內(nèi)容就不再需要了,可以被刪除了。

  • 由于每次事務(wù)修改都要落盤到數(shù)據(jù)庫(kù)文件,這些落盤操作涉及到多次磁盤尋道,即一次事務(wù)多次隨機(jī)磁盤尋道,這樣代價(jià)其實(shí)是很大的。

  • 當(dāng)需要事務(wù)回滾的功能時(shí),頁(yè)面管理器就可以從 Journal 文件中讀出來(lái)舊頁(yè)面內(nèi)容覆蓋回去。

  • 雖然這個(gè)算法很簡(jiǎn)單,但是缺陷也明顯:它沒(méi)有任何的讀寫并發(fā)支持。每次開始一個(gè)寫事務(wù),從開始寫事務(wù),到這個(gè)寫事務(wù)提交完成的過(guò)程中間,其他的讀寫事務(wù)都不能開始,可以說(shuō)是“一寫全卡住”。

WAL

從上面的分析可以看出,以 Journal 文件的機(jī)制,每次寫事務(wù):

  • 需要把內(nèi)容修改全部落盤到數(shù)據(jù)庫(kù)文件才算完成。

  • 這個(gè)過(guò)程中間,不能同時(shí)存在其他并發(fā)的讀、寫操作。

從 sqlite3.7.0 版本開始(SQLite Release 3.7.0 On 2010-07-211,sqlite 引入了更常見的 WAL 機(jī)制來(lái)解決頁(yè)面的讀寫并發(fā)問(wèn)題,WAL 的原理如下圖所示:

WAL 機(jī)制中,事務(wù)對(duì)頁(yè)面的修改:

  • 并沒(méi)有馬上落到數(shù)據(jù)庫(kù)文件里,而是首先寫入 WAL 文件中。這樣有兩個(gè)好處:


    • ?WAL文件是 append-only 的文件,在文件結(jié)尾處添加新內(nèi)容,對(duì)寫磁盤文件這種操作而言是更快的,因?yàn)樯倭撕芏啻疟P尋道的流程。

    • 有了 WAL 之后,讀寫并發(fā)有了一些改善:由于事務(wù)的修改并沒(méi)有馬上落盤到數(shù)據(jù)庫(kù)文件,所以就不可見,后續(xù)如果需要回滾事務(wù)的修改也更容易:不要這個(gè)事務(wù)修改的那部分 WAL 內(nèi)容即可。

  • 由于修改有時(shí)候還未落盤,需要維護(hù)一個(gè) wal 中頁(yè)面的索引,用于根據(jù)頁(yè)面編號(hào)定位到 WAL 中的頁(yè)面。由于 wal 索引可以控制哪些 wal 文件內(nèi)容“可見”,于是就能控制未提交的事務(wù)修改對(duì)讀操作并不可見了。

  • WAL 文件不能一直增長(zhǎng)下去,需要定期把 WAL 文件中已經(jīng)提交的事務(wù)修改內(nèi)容落盤到數(shù)據(jù)庫(kù)文件,這個(gè)流程被稱為“checkpoint”。在“checkpoint”之后,wal 索引就可以修改了。雖然 checkpoint 過(guò)程將 WAL 文件中的內(nèi)容落盤到數(shù)據(jù)庫(kù)文件,仍然是針對(duì)數(shù)據(jù)庫(kù)文件的隨機(jī)寫流程,有很多磁盤尋道操作,但是由于一次 checkpoint 累計(jì)了多次寫事務(wù)一次性落盤,代價(jià)小了一些。

雖然同一時(shí)間仍然只能有一個(gè)寫事務(wù)在進(jìn)行,但是讀事務(wù)同時(shí)存在多個(gè)。其核心原因是因?yàn)樾薷牟](méi)有馬上直接落盤到數(shù)據(jù)庫(kù)文件中,這樣修改的可見性就可以由 wal 索引來(lái)控制,即:寫事務(wù)盡管寫,讀事務(wù)盡管讀,只要控制這些寫事務(wù)的修改不在 wal 索引中可見即可。WAL 雖然支持“一寫多讀”,而不是 Journal 文件那樣的“一寫全卡住”,但是還有一個(gè)問(wèn)題沒(méi)有解決:在做 checkpoint 操作的時(shí)候,連寫事務(wù)也不能進(jìn)行了。

兩個(gè)可能的優(yōu)化方案

以下介紹 sqlite 目前在討論的兩個(gè)優(yōu)化方案,之所以說(shuō)是“可能”,因?yàn)榭催@部分代碼還并沒(méi)有合并到主干中,目前暫時(shí)還在分支里,參見:https://github.com/sqlite/sqlite/tree/begin-concurrent-pnu-wal2。

WAL2:

為了解決“checkpoint”時(shí)無(wú)法進(jìn)行寫事務(wù)”的痛點(diǎn),sqlite 目前在嘗試新的 WAL-2 機(jī)制。

引入 WAL-2 之后,同時(shí)有兩個(gè) WAL 文件,這樣可以:checkpoint 其中一個(gè) WAL 文件時(shí),繼續(xù)寫另一個(gè) WAL 文件,下一次再進(jìn)行 checkpoint 時(shí)進(jìn)行切換,這樣 checkpoint 就不會(huì)阻塞住寫操作。

BEGIN CONCURRENT:

目前的 WAL 機(jī)制,都只能支持同一時(shí)間一個(gè)寫事務(wù),BEGIN CONCURRENT 機(jī)制可以實(shí)現(xiàn)多個(gè)寫并發(fā),這篇?SQLite: Begin Concurrent?文檔中,大概描述了一下這個(gè)優(yōu)化的思路:

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.

簡(jiǎn)單的理解上面的這段話:

  • 不同的寫事務(wù),如果操作的是不同的表,不同的表數(shù)據(jù)雖然物理上在同一個(gè)數(shù)據(jù)庫(kù)文件,但是邏輯上卻分屬于不同的 B-Tree,這樣不同的 B-Tree 管理的頁(yè)面之間就不會(huì)發(fā)生沖突,頂多在落盤到數(shù)據(jù)庫(kù)文件的時(shí)候加鎖即可。

  • 其次,即便多個(gè)寫事務(wù)操作了同樣的表,但只要同一張表的鍵值離得較遠(yuǎn),發(fā)生沖突的可能性就不大。一旦在事務(wù)提交的時(shí)候發(fā)現(xiàn)有沖突,那么就從頭開始再做一次事務(wù),直到可以提交時(shí)沒(méi)有沖突成功提交為止。后面這個(gè)沖突解決的思路實(shí)際上文檔中并沒(méi)有,是我自己根據(jù)其他論文想出來(lái)的一個(gè)辦法:)。

目前這兩個(gè)優(yōu)化,由于還并沒(méi)有合并到主干,所以我也還沒(méi)有具體看實(shí)現(xiàn),后續(xù)體現(xiàn)在 sqlite 主干中的存儲(chǔ)引擎方面的優(yōu)化,再梳理出來(lái)。

引用鏈接

[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實(shí)現(xiàn)(三)- journal文件備份機(jī)制 - codedump的網(wǎng)絡(luò)日志:

?https://www.codedump.info/post/20211222-sqlite-btree-3-journal
[4]?sqlite3.36版本 btree實(shí)現(xiàn)(四)- WAL的實(shí)現(xiàn) - codedump的網(wǎng)絡(luò)日志:?

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

關(guān)于 Databend

Databend 是一款開源、彈性、低成本,基于對(duì)象存儲(chǔ)也可以做實(shí)時(shí)分析的新式數(shù)倉(cāng)。期待您的關(guān)注,一起探索云原生數(shù)倉(cāng)解決方案,打造新一代開源 Data Cloud。

  • Databend 文檔:https://databend.rs/

  • Twitter:https://twitter.com/Datafuse_Labs

  • Slack:https://datafusecloud.slack.com/

  • Wechat:Databend

  • GitHub :https://github.com/datafuselabs/databend

?文章首發(fā)于公眾號(hào):Databend


Sqlite 并發(fā)讀寫的演進(jìn)之路的評(píng)論 (共 條)

分享到微博請(qǐng)遵守國(guó)家法律
冀州市| 梧州市| 班戈县| 吉林市| 靖远县| 宜阳县| 孝昌县| 民乐县| 商都县| 大余县| 祥云县| 扎兰屯市| 阜新市| 辉县市| 门源| 忻城县| 湘乡市| 和林格尔县| 竹北市| 晋城| 伊宁县| 资源县| 旅游| 临澧县| 塔城市| 阿拉尔市| 喀喇沁旗| 富民县| 西林县| 宣城市| 丰顺县| 营山县| 永城市| 元氏县| 汨罗市| 太和县| 建阳市| 广宗县| 日照市| 清涧县| 略阳县|