SQLite 數(shù)據(jù)庫(kù) WAL 工作模式原理簡(jiǎn)介

閃存
  • 對(duì)象序列化系列

Android 對(duì)象序列化之你不知道的 Serializable
Android 對(duì)象序列化之 Parcelable 深入分析
Android 對(duì)象序列化之追求完美的 Serial

  • 數(shù)據(jù)序列化系列(待更)

《Android 數(shù)據(jù)序列化之 JSON》
《Android 數(shù)據(jù)序列化之 Protocol Buffer 使用》
《Android 數(shù)據(jù)序列化之 Protocol Buffer 源碼分析》

  • SQLite 存儲(chǔ)系列

Android 存儲(chǔ)選項(xiàng)之 SQLiteDatabase 創(chuàng)建過(guò)程源碼分析
Android 存儲(chǔ)選項(xiàng)之 SQLiteDatabase 源碼分析
數(shù)據(jù)庫(kù)連接池 SQLiteConnectionPool 源碼分析
SQLiteDatabase 啟用事務(wù)源碼分析
SQLite 數(shù)據(jù)庫(kù) WAL 模式工作原理簡(jiǎn)介
SQLite 數(shù)據(jù)庫(kù)鎖機(jī)制與事務(wù)簡(jiǎn)介
SQLite 數(shù)據(jù)庫(kù)優(yōu)化那些事兒


SQLite WAL 模式工作原理分析

什么是 WAL

WAL 的全稱是 Write Ahead Logging(預(yù)寫(xiě)日志),它是很多數(shù)據(jù)庫(kù)中用于實(shí)現(xiàn)原子事務(wù)的一種機(jī)制。SQLite 在 3.7.0 版本引入該特性,在此之前 SQLite 實(shí)現(xiàn)原子提交和回滾的方法是 rollback journal(回滾日志)。

WAL 工作原理
  • Rollback Journal

SQLite 在引入 WAL 模式之前,使用的是 Rollback Journal 機(jī)制實(shí)現(xiàn)原子事務(wù)。Rollback Journal 的原理是,在修改數(shù)據(jù)庫(kù)文件中數(shù)據(jù)之前,先將修改所在分頁(yè)中的數(shù)據(jù)備份在另外一個(gè)地方,然后才將修改寫(xiě)入到數(shù)據(jù)庫(kù)文件中;如果事務(wù)失敗,則將備份數(shù)據(jù)拷貝回來(lái),撤銷修改;如果事務(wù)成功,則刪除備份數(shù)據(jù)提交修改。

  • WAL

WAL 的方法正好反過(guò)來(lái)了,原始內(nèi)容保留在數(shù)據(jù)庫(kù)文件中,修改數(shù)據(jù)而是寫(xiě)入到另外一個(gè)稱為 WAL 的文件中,如果事務(wù)失敗,WAL 中的記錄會(huì)被忽略,撤銷修改;如果事務(wù)成功,它將隨后的某個(gè)時(shí)間被寫(xiě)回到數(shù)據(jù)庫(kù)文件中,提交修改。

同步 WAL 文件和數(shù)據(jù)庫(kù)文件的行為被稱為 CheckPoint(檢查點(diǎn))。

在讀數(shù)據(jù)庫(kù)時(shí),SQLite 將在 WAL 文件中搜索,找到最后一個(gè)寫(xiě)入點(diǎn),記住它,并忽略在此之后的寫(xiě)入數(shù)據(jù)(這保證了讀和寫(xiě)可以并發(fā)執(zhí)行);隨后它確定所要讀的數(shù)據(jù)所在頁(yè)是否在 WAL 文件中,如果在,則讀 WAL 文件中的數(shù)據(jù),如果不在,則直接讀數(shù)據(jù)庫(kù)文件中的數(shù)據(jù)。

在寫(xiě)入數(shù)據(jù)庫(kù)時(shí),SQLite 將數(shù)據(jù)寫(xiě)入到 WAL 文件中即可,但是必須保證獨(dú)占寫(xiě)入,因此寫(xiě)和寫(xiě)之間不能并發(fā)執(zhí)行。WAL 在實(shí)現(xiàn)的過(guò)程中,使用了共享內(nèi)存技術(shù),因此所有的讀寫(xiě)進(jìn)程必須在同一個(gè)機(jī)器上,否則無(wú)法保證數(shù)據(jù)的一致性。

檢查點(diǎn)

當(dāng)然,最終希望將 WAL 文件中附加的所有事務(wù)轉(zhuǎn)移回原始數(shù)據(jù)庫(kù)。將 WAL 文件事務(wù)移回?cái)?shù)據(jù)庫(kù)稱為“檢查點(diǎn)”。

考慮回滾日志和預(yù)寫(xiě)日志之間差異的另一種方法是,在回滾日志方法中,存在兩種原始操作,即讀取和寫(xiě)入,而對(duì)于預(yù)寫(xiě)日志,此時(shí)會(huì)有三種原始操作:讀取、寫(xiě)入和檢查點(diǎn)。

默認(rèn)情況下,當(dāng) WAL 文件達(dá)到 1000 頁(yè)的閾值大小時(shí),SQLite 自動(dòng)執(zhí)行一個(gè)檢查點(diǎn)。使用 WAL 的應(yīng)用程序不需要執(zhí)行任何附加操作即可享受到 CheckPoint。但是如果需要應(yīng)用程序調(diào)整自動(dòng)檢查點(diǎn)閾值,可以使用 SQLITE_DEAULT_WAL_AUTOCHECKPOINT 編譯時(shí)選項(xiàng)指定其它默認(rèn)值。

并發(fā)

在 WAL 模式數(shù)據(jù)庫(kù)上開(kāi)始讀數(shù)據(jù)庫(kù)操作時(shí),它首先會(huì)記住 WAL 中最后一個(gè)有效提交記錄的位置。將此點(diǎn)稱為“結(jié)束標(biāo)記”。由于 WAL 可以在各種讀取器連接到數(shù)據(jù)庫(kù)的同時(shí)不斷增長(zhǎng)并添加新的提交記錄,因此每個(gè)讀取器都可能具有自己的結(jié)束標(biāo)記。但是對(duì)于任何特定的讀取,結(jié)束標(biāo)記在事務(wù)期間不會(huì)改變,從而確保了單個(gè)讀取事務(wù)只能看到數(shù)據(jù)庫(kù)內(nèi)容,因?yàn)樗嬖谟趩蝹€(gè)時(shí)間點(diǎn)。

當(dāng)讀取需要一頁(yè)內(nèi)容時(shí),它首先檢查 WAL 以查看讀取頁(yè)面是否出現(xiàn)在該頁(yè)面中,如果是,它將拉入 WAL 中出現(xiàn)在讀者終點(diǎn)標(biāo)記之前的頁(yè)面的最后副本。如果在 WAL 中沒(méi)有頁(yè)面的副本位于讀者的結(jié)尾標(biāo)記之前,則從原始數(shù)據(jù)庫(kù)文件中讀取頁(yè)面。讀取可以存在于單獨(dú)的進(jìn)程中,因此避免強(qiáng)迫每個(gè)讀取掃描整個(gè) WAL 尋找頁(yè)面(WAL 文件可以增長(zhǎng)到數(shù)兆字節(jié),具體取決于運(yùn)行檢查點(diǎn)的頻率),這種數(shù)據(jù)結(jié)構(gòu)成為“wal-index”保留在共享內(nèi)存中,這有助于讀取以最少的 I/O 速度在 WAL 中定位頁(yè)面。wal-index 大大提高了讀取的性能,但是共享內(nèi)存的使用意味著所有讀取者必須在同一臺(tái)計(jì)算機(jī)上。

寫(xiě)入者僅將新內(nèi)容附加到 WAL 文件的末尾。因?yàn)閷?xiě)入不做任何會(huì)干擾讀取數(shù)據(jù)行為的事情,所以寫(xiě)和讀可以同時(shí)運(yùn)行。但是,由于只有一個(gè) WAL 文件,所以一次只能有一個(gè)寫(xiě)入器。

檢查點(diǎn)操作從 WAL 文件中獲取內(nèi)容,并將其回傳到原始數(shù)據(jù)文件中。檢查點(diǎn)可以與讀取任務(wù)同時(shí)運(yùn)行,但是當(dāng)檢查點(diǎn)到達(dá) WAL 中的頁(yè)面,超過(guò)任何當(dāng)前讀取任務(wù)的結(jié)束標(biāo)記時(shí),必須停止。檢查點(diǎn)必須在該點(diǎn)處停止,因?yàn)樗赡軙?huì)覆蓋讀取者正在使用的部分?jǐn)?shù)據(jù)庫(kù)文件。該檢查點(diǎn)會(huì)記?。╳al-index中)到達(dá)的距離,并繼續(xù)將內(nèi)容從 WAL 傳輸?shù)较乱淮握{(diào)用中斷位置的數(shù)據(jù)庫(kù)。

因此,長(zhǎng)時(shí)間運(yùn)行的讀取事務(wù)可能會(huì)阻止檢查指針運(yùn)行。但是大概每個(gè)讀取事務(wù)最終都會(huì)結(jié)束,并且檢查指針將能夠繼續(xù)。

每當(dāng)發(fā)生寫(xiě)操作時(shí),寫(xiě)入者都會(huì)檢查指針取得了多少進(jìn)展,如果整個(gè) WAL 已傳輸?shù)綌?shù)據(jù)庫(kù)并進(jìn)行了同步,并且如果沒(méi)有讀取正在使用 WAL,則寫(xiě)入者將 WAL 重新從 0 開(kāi)始,并在 WAL 開(kāi)始時(shí)進(jìn)行新交易。這種機(jī)制可以防止 WAL 文件無(wú)限制地增長(zhǎng)。

性能問(wèn)題

寫(xiě)事務(wù)非???,絕大多數(shù)情況下,WAL 會(huì)提高 SQLite 的事務(wù)性能,因?yàn)樗鼈冎簧婕耙淮螌?xiě)入內(nèi)容(相對(duì)回滾日志事務(wù)而言則是兩次),而且寫(xiě)入都是順序的。但是在某些極端情況下,卻會(huì)導(dǎo)致 SQLite 事務(wù)的性能下降。

但是隨著 WAL 文件大小的增加,讀取性能會(huì)下降,因?yàn)槊總€(gè)讀取任務(wù)都必須檢查 WAL 文件中的內(nèi)容,并且檢查 WAL 文件所需要的時(shí)間與 WAL 文件的大小成正比。wal-index 有助于更快地在 WAL 文件中查找內(nèi)容,但是性能隨著 WAL 文件大小的增加而下降。因此為了保持良好的讀取性能,要通過(guò)定期運(yùn)行檢查點(diǎn)來(lái)減小 WAL 文件的大小。

檢查點(diǎn)確實(shí)需要同步操作,以避免斷點(diǎn)或硬重啟后數(shù)據(jù)庫(kù)損壞的可能性。在將內(nèi)容從 WAL 移到數(shù)據(jù)庫(kù)之前,必須將 WAL 同步到持久性存儲(chǔ),并且在重置 WAL 之前必須同步數(shù)據(jù)庫(kù)文件。CheckPoint 還需要更多尋求。檢查指針會(huì)盡最大努力對(duì)數(shù)據(jù)庫(kù)進(jìn)行順序的頁(yè)面寫(xiě)入,但是即使這樣,頁(yè)面寫(xiě)入之間通常也會(huì)散布著許多查找操作。這些因素共同導(dǎo)致檢查點(diǎn)比寫(xiě)入事務(wù)慢。

默認(rèn)策略是允許連續(xù)的寫(xiě)事務(wù)增長(zhǎng) WAL,直到 WAL 變?yōu)榇蠹s 1000 頁(yè),然后為每個(gè)后續(xù) COMMIT 運(yùn)行檢查點(diǎn)操作,直到 WAL 重置為小于 1000 頁(yè)為止。默認(rèn)情況下,檢查點(diǎn)將由執(zhí)行 COMMIT 的同一線程自動(dòng)運(yùn)行,該線程是 WAL 超過(guò)其大小限制。這會(huì)導(dǎo)致大多數(shù) COMMIT 操作非???,但偶爾的 COMMIT(觸發(fā)檢查點(diǎn)的操作)會(huì)慢的多。如果不希望這種影響,則應(yīng)用程序可以禁用自動(dòng)檢查點(diǎn),并在單獨(dú)的線程或單獨(dú)的進(jìn)程中運(yùn)行定期檢查點(diǎn)。

WAL 的優(yōu)點(diǎn)與缺點(diǎn)

優(yōu)點(diǎn):

  1. 讀操作不會(huì)阻塞寫(xiě)操作,同時(shí)寫(xiě)操作也不會(huì)阻塞讀操作。這是并發(fā)管理的“黃金準(zhǔn)則”。
  2. 在大多數(shù)操作場(chǎng)景中,與回滾日志相比,WAL 相當(dāng)快。
  3. 磁盤(pán) I/O 變得更可預(yù)見(jiàn),更少的 fsync 系統(tǒng)調(diào)用,因?yàn)樗械?WAL 寫(xiě)操作是線性寫(xiě)入日志文件,很多 I/O 變的連續(xù)并能夠按計(jì)劃執(zhí)行。

缺點(diǎn):

  1. 所有的處理被綁定到單個(gè)主機(jī)上。也就是說(shuō),不能再如 NFS 這樣的網(wǎng)絡(luò)文件系統(tǒng)上使用 WAL。
  2. 為滿足 WAL 和相關(guān)共享內(nèi)存的需要,使用 WAL 引入了里兩個(gè)額外的半持久性文件-wal 和-shm。對(duì)于那些使用SQLite 數(shù)據(jù)庫(kù)作為應(yīng)用程序文件格式是不具有吸引力的。這也影響了只讀環(huán)境,因?yàn)?shm文件必須是可寫(xiě)的,并且/或數(shù)據(jù)庫(kù)所在目錄也必須是可寫(xiě)的。
  3. 對(duì)于非常大的事務(wù),WAL 的性能將會(huì)降低。雖然 WAL 是一個(gè)高性能選項(xiàng),但是非常大或運(yùn)行時(shí)間非常長(zhǎng)的事務(wù)會(huì)引入額外的開(kāi)銷。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容