天天看點

Sqlite學習筆記(四)&&SQLite-WAL原理(轉)

WAL是什麼

      WAL(Write ahead logging)是一種日志模式,它是一種思想,普遍應用于關系型資料庫。每個事務執行變更時,修改資料頁,同時會産生日志,這樣在事務送出後,不需要将修改的髒頁刷盤,隻需要将事務産生的日志落盤即可傳回。WAL保證日志一定先于對應的髒頁落盤,就是所謂的WAL。SQLITE在3.7版本以後引入WAL,它的WAL也基本采用這個原理,隻不過SQLite實作比較簡單,日志記錄的是修改後的頁,而不是所謂的修改日志。WAL模式下,SQlite中除了db檔案,還包含了兩個檔案,.wal檔案和.shm檔案,前者是日志檔案,後者是日志索引檔案。

日志模式

      SQLite中日志模式主要有DELETE和WAL兩種,其他幾種比如TRUNCATE,PERSIST,MEMORY基本原理都與DELETE模式相同,不作詳細展開。DELETE模式下,日志中記錄的變更前資料頁内容;WAL模式下,日志中記錄的是變更後的資料頁内容。事務送出時,DELETE模式将日志刷盤,将DB檔案刷盤,成功後,再将日志檔案清理;WAL模式則是将日志檔案刷盤,即可完成送出過程。那麼WAL模式下,資料檔案何時更新呢?這裡引入了檢查點概念,檢查點的作用就是定期将日志中的新頁覆寫DB檔案中的老頁,并通過參數wal_autocheckpoint來控制檢查點時機,達到權衡讀寫的目的。

     DELETE模式下,寫事務直接更新db-page,并将old-page寫入日志,讀事務則直接讀db-page,因為db-page中儲存了送出的所有事務的更新。事務送出後,直接将日志檔案删除;若事務需要復原,則将日志中old-page中的内容覆寫db-page,恢複原始内容。WAL模式下,寫事務将更新寫到日志檔案中,不更新db-page,事務送出時,也不影響db-page,隻是将日志持久化而已。若事務復原,則不将日志寫入檔案即可。由于最新的資料在日志檔案中,那麼如何讀取到最新的資料呢?WAL模式通過end-mark(事務送出位點)達到這一目的。具體而已,事務開始時,會首先掃描日志檔案,擷取最近一個end-mark,在讀取資料時,首先會判斷page是否則在wal日志檔案中存在,因為同一個page,一定是wal檔案中的比db檔案中的要新。如果存在,則使用,否則,再從db檔案中擷取指定的page。從流程上來看,這個過程比較慢,因為極端情況下,每次讀都需要掃描wal檔案和db檔案。為了提高性能,WAL模式中有一個wal-index檔案,這個檔案記錄了頁号和該頁在WAL檔案中的偏移,并且wal-index檔案采用共享緩存實作,從檔案名也可以看到,字尾是.shm,是以判斷page是否在wal檔案存在的操作實質是一次記憶體讀。wal-index采用hash表存儲,是以查詢效率也非常高。

      與傳統的DBMS不同,SQLite中記錄的日志,實質是dirty-page,重做實質是對利用WAL中的日志頁覆寫db-page,這種實作方式比較簡單,同時也比較浪費空間,因為一個page是1k,即使隻更新1byte,也會導緻日志記錄1k。

WAL的優勢與劣勢

1)  并發優勢  

      SQLite為什麼引入WAL,一定是WAL有很多好的特性。其中最主要的一點是WAL支援讀寫并發。在DELETE模式下,讀寫是互斥的。為什麼WAL可以并發,而DELETE不行?我這裡不打算詳細展開WAL模式和DELETE模式的鎖機制,後面有機會再單獨寫這一部分。從上面一節的分析可以知道,WAL模式下,寫事務以append方式記錄new-page,而讀事務隻會讀取db-page和end-mark之前的wal日志,是以不會發生讀寫沖突的問題,讀寫可以并發。而DELETE模式下,寫事務寫的是db-page,讀事務也是讀db-page,是以讀寫不能并發。

2) 寫性能優勢

從前面的分析可知,WAL模式下,事務送出隻需要寫入日志檔案即可,為了持久化,隻需要一次fsync調用。而DELETE模式下,事務送出過程中,首先要確定日志落盤(儲存old-page,用來rollback),這裡需要一次fsync調用,然後再執行db檔案刷盤,這裡還需要一次fsync,并且修改的db-page可能是離散的,是以可能需要多餘一次的fsync。此外,WAL寫日志都是順序寫,相對于離散寫又有很大的優勢。是以DELETE模式下寫性能會比WAL模式要差。測試結果也證明了這一點,這裡可以參考測試報告。

3) WAL劣勢

      開啟WAL後,每次讀取page,都需要通過wal-index來确認page是否在WAL中,這個會産生一定的性能損耗。另外,會引入WAL檔案,這個檔案如果使用不當,可能會急劇膨脹,WAL檔案變大後,意味着檢索wal-index的代價也變高。而且由于SQLite一般用于端裝置,空間也比較稀缺,是以要嚴格控制好WAL檔案的大小。此外,WAL的索引檔案采用共享記憶體實作,是以通路SQlite的程序不能跨機器。

開啟WAL模式

      通過指令pragma journal_mode=wal可以開啟wal模式。前面我們提到開啟WAL模式後,如果使用不當,可能導緻WAL檔案空間暴增,但我們有辦法避免這種情況發生。這裡主要介紹兩個參數,wal_autocheckpoint和journal_size_limit。wal_autocheckpoint用來設定觸發檢查點的時機,預設是1000頁,即當日志增長到1000頁時,開始做檢查點操作。這裡要說明一點的是,SQLite中沒有單獨的檢查點線程,如果設定1000,則觸發寫1000頁的事務來進行檢查點操作。是以這個事務的響應時間會比較長,而其它事務則不受影響。用來設定日志檔案的大小,預設情況為-1,當這個參數設定時,若累計更新頁大小超過journal_size_limit,也會導緻檢查點觸發,用以重複利用日志檔案,避免日志繼續增長。

相關參數

1)  journal_mode(日志模式)

預設是DELETE模式

DELETE:原始資料頁存放在日志檔案中,事務送出時,将檔案删除。

TRUNCATE :與DELETE模式的差別是,清空日志檔案,但不删除檔案清空檔案往往比删除檔案要快。

PERSIST:與DELETE和TRUNCATE模式差別是,既不删除檔案,也不清空檔案,而是将日志檔案第一個頁設定标記(置0),這個也是為了提高性能。 MEMORY :記憶體模式,修改不落盤,無法保證事務的原子性。

OFF:不開啟日志,這樣沒法保證事務的原子性。

WAL :write ahead log,3.7.0引入,日志中記錄修改頁,送出時隻需刷修改頁。

2)  journal_size_limit(日志檔案大小)

預設值為-1,表示沒有限制,機關是位元組。 DELETE模式下,當日志增長超過閥值時,則進行截斷。 WAL模式下,當日志增長超過閥值時,日志檔案不再會被截斷,而是重複利用, 因為通常情況下重複寫的性能要好于追加的性能,而且也省磁盤空間。 default_journal_size_limit,用于設定日志檔案的預設大小。

3)  wal_checkpoint(檢查點模式)

PASSIVE,預設自動檢查點和主動檢查點都是PASSIVE類型,将所有可以同步到db的資料都進行同步(不超過所有線程的end mark),不持有排它鎖,是以不會影響其他讀寫事務。

FULL,将wal與db檔案完全同步,需要等待所有讀寫事務都結束,并且會堵塞新的讀寫事務

RESTART,與FULL模式的差別是,下一個寫線程從頭開始寫wal檔案。 TRUNCATE,與FULL模式的差別是,将wal檔案截斷為0。

4) wal_autocheckpoint(檢查點觸發時機)

預設值為1000頁,機關是頁。當日志的增量到N頁時,觸發檢查點操作,将wal_autocheckpoint設定為0或者-1,表示關閉檢查點。

5) synchronous(同步模式)

預設設定是FULL

0(OFF):事務送出時,不作sync操作,直接傳回。

1(NORMAL):事務送出時,不作sync操作

2(FULL):每次事務送出,都強制刷日志(WAL),強制資料頁(journal)

6) cache_size 預設值2000,機關為頁 修改db的緩存頁數目,臨時生效

7) default_cache_size

預設值2000,機關為頁

修改緩存頁數目,永久生效若同時設定了cache_size和default_cache_size,以default_cache_size為準

參考文檔

<a href="https://www.sqlite.org/wal.html">https://www.sqlite.org/wal.html</a>

http://www.cnblogs.com/cchust/p/4754619.html

繼續閱讀