SQLite學習手冊(臨時文件)

1、簡介:

    儘管SQLite的數據庫是由單一文件構成,然而事實上在SQLite運行時卻存在着一些隱含的臨時文件,這些臨時文件是出於不一樣的目的而存在的,對於開發者而言,它們是透明的,所以在開發的過程當中咱們並不須要關注它們的存在。儘管如此,若是能對這些臨時文件的產生機制和應用場景有着很好的理解,那麼對咱們從此應用程序的優化和維護都是極有幫助的。在SQLite中主要產生如下七種臨時文件,如:
    1). 回滾日誌。
    2). 主數據庫日誌。
    3). SQL語句日誌。
    4). 臨時數據庫文件。
    5). 視圖和子查詢的臨時持久化文件。
    6). 臨時索引文件。
    7). VACUUM命令使用的臨時數據庫文件。
    
2、具體說明:

     1. 回滾日誌:
    SQLite爲了保證事物的原子性提交和回滾,在事物開始時建立了該臨時文件。此文件始終位於和數據庫文件相同的目錄下,其文件名格式爲:  數據庫文件名 + "-journal" 。換句話說,若是沒有該臨時文件的存在,當程序運行的系統出現任何故障時,SQLite將沒法保證事物的完整性,以及數據狀態的一致性。該文件在事物提交或回滾後將被馬上刪除。
    在事物運行期間,若是當前主機因電源故障而宕機,而此時因爲回滾日誌文件已經保存在磁盤上,那麼當下一次程序啓動時,SQLite在打開數據庫文件的過程當中將會發現該臨時文件的存在,咱們稱這種日誌文件爲"Hot Journal"。SQLite會在成功打開數據庫以前先基於該文件完成數據庫的恢復工做,以保證數據庫的數據回覆到上一個事物開始以前的狀態。
    在SQLite中咱們能夠經過修改 journal_mode pragma ,而使SQLite對維護該文件採用不一樣的策略。缺省狀況下該值爲 DELETE ,即在事物結束後刪除日誌文件。而PERSIST選項值將不會刪除日誌文件,而是將回滾日誌文件的頭部清零,從而避免了文件刪除所帶的磁盤開銷。再有就是OFF選項值,該值將指示SQLite在開始事物時不產生回滾日誌文件,這樣一旦出現系統故障,SQLite也沒法再保障數據庫數據的一致性。

    2. 主數據庫日誌:
    在SQLite中,若是事物的操做做用於多個數據庫,即經過ATTACH命令附加到當前鏈接中的數據庫,那麼SQLite將生成主數據庫日誌文件以保證事物產生的改變在多個數據庫之間保持原子性。和回滾日誌文件同樣,主數據庫日誌文件也位於當前鏈接中主數據庫文件所處的目錄內,其文件名格式爲:主數據庫文件名 + 隨機的後綴。在該文件中,將包含全部當前事物將會改變的Attached數據庫的名字。在事物被提交以後,此文件亦被SQLite隨之刪除。
    主數據庫日誌文件只有在某一事物同時操做多個數據庫時(主數據庫和Attached數據庫)纔有可能被建立。經過該文件,SQLite能夠實現跨多個數據庫的事物原子性,不然,只能簡單的保證每一個單一的數據庫內的狀態一致性。換句話說,若是該事物在執行的過程當中出現系統崩潰或主機宕機的現象,在進行數據恢復時,若沒有該文件的存在,將會致使部分SQLite數據庫處於提交狀態,而另一部分則處於回滾狀態,所以該事物的一致性將被打破。

     3. SQL語句日誌:
    在一個較大的事物中,SQLite爲了保證部分數據在出現錯誤時能夠被正常回滾,因此在事物開始時建立了SQL語句日誌文件。好比,update語句修改了前50條數據,然而在修改第51條數據時發現該操做將會破壞某字段的惟一性約束,最終SQLite將不得不經過該日誌文件回滾已經修改的前50條數據。
    SQL語句日誌文件只有在INSERT或UPDATE語句修改多行記錄時纔有可能被建立,與此同時,這些操做還極有可能會打破某些約束並引起異常。可是若是INSERT或UPDATE語句沒有被包含在BEGIN...COMMIT中,同時也沒有任何其它的SQL語句正在當前的鏈接上運行,在這種狀況下,SQLite將不會建立SQL語句日誌文件,而是簡單的經過回滾日誌來完成部分數據的UNDO操做。
    和上面兩種臨時文件不一樣的是,SQL語句日誌文件並不必定要存儲在和數據庫文件相同的目錄下,其文件名也是隨機生成。該文件所佔用的磁盤空間須要視UPDATE或INSERT語句將要修改的記錄數量而定。在事物結束後,該文件將被自動刪除。

     4. 臨時數據庫文件:
    當使用"CREATE TEMP TABLE"語法建立臨時數據表時,該數據表僅在當前鏈接內可見,在當前鏈接被關閉後,臨時表也隨之消失。然而在生命期內,臨時表將連同其相關的索引和視圖均會被存儲在一個臨時的數據庫文件以內。該臨時文件是在第一次執行"CREATE TEMP TABLE"時即被建立的,在當前鏈接被關閉後,該文件亦將被自動刪除。最後須要說明的是,臨時數據庫不能被執行DETACH命令,同時也不能被其它進程執行ATTACH命令。
    
     5. 視圖和子查詢的臨時持久化文件:
    在不少包含子查詢的查詢中,SQLite的執行器會將該查詢語句拆分爲多個獨立的SQL語句,同時將子查詢的結果持久化到臨時文件中,以後在基於該臨時文件中的數據與外部查詢進行關聯,所以咱們能夠稱其爲物化子查詢。一般而言,SQLite的優化器會盡力避免子查詢的物化行爲,可是在有些時候該操做是沒法避免的。該臨時文件所佔用的磁盤空間須要依賴子查詢檢索出的數據數量,在查詢結束後,該文件將被自動刪除。見以下示例:
     SELECT * FROM ex1 WHERE ex1.a IN (SELECT b FROM ex2);
    在上面的查詢語句中,子查詢SELECT b FROM ex2的結果將會被持久化到臨時文件中,外部查詢在運行時將會爲每一條記錄去檢查該臨時文件,以判斷當前記錄是否出如今臨時文件中,若是是則輸出當前記錄。顯而易見的是,以上的行爲將會產生大量的IO操做,從而顯著的下降了查詢的執行效率,爲了不臨時文件的生成,咱們能夠將上面的查詢語句改成:
     SELECT * FROM ex1 WHERE EXISTS(SELECT 1 FROM ex2 WHERE ex2.b=ex1.a);
    對於以下查詢語句,若是SQLite不作任何智能的rewrite操做,該查詢中的子查詢也將會被持久化到臨時文件中,如:
     SELECT * FROM ex1 JOIN (SELECT b FROM ex2) AS t ON t.b=ex1.a;
    在SQLite自動將其修改成下面的寫法後,將不會再生成臨時文件了,如:
     SELECT ex1.*, ex2.b FROM ex1 JOIN ex2 ON ex2.b=ex1.a;

     6. 臨時索引文件:
    當查詢語句包含如下SQL從句時,SQLite爲存儲中間結果而建立了臨時索引文件,如:
    1). ORDER BY或GROUP BY從句。
    2). 彙集查詢中的DISTINCT關鍵字。
    3). 由UNION、EXCEPT和INTERSECT鏈接的多個SELECT查詢語句。
    須要說明的是,若是在指定的字段上已經存在了索引,那麼SQLite將不會再建立該臨時索引文件,而是經過直接遍歷索引來訪問數據並提取有用信息。若是沒有索引,則須要將排序的結果存儲在臨時索引文件中以供後用。該臨時文件所佔用的磁盤空間須要依賴排序數據的數量,在查詢結束後,該文件被自動刪除。

     7. VACUUM命令使用的臨時數據庫文件:
    VACUUM命令在工做時將會先建立一個臨時文件,而後再將重建的整個數據庫寫入到該臨時文件中。以後再將臨時文件中的內容拷貝回原有的數據庫文件中,最後刪除該臨時文件。
    該臨時文件所佔用的磁盤空間不會超過原有文件的尺寸。

3、相關的編譯時參數和指令:

    對於SQLite來講,回滾日誌、主數據庫日誌和SQL語句日誌文件在須要的時候SQLite都會將它們寫入磁盤文件,可是對於其它類型的臨時文件,SQLite是能夠將它們存放在內存中以取代磁盤文件的,這樣在執行的過程當中就能夠減小大量的IO操做了。要完成該優化主要依賴於如下三個因素:
     1. 編譯時參數SQLITE_TEMP_STORE:
    該參數是源代碼中的宏定義(#define),其取值範圍是0到3(缺省值爲1),見以下說明:
    1). 等於0時,臨時文件老是存儲在磁盤上,而不會考慮temp_store pragma指令的設置。
    2). 等於1時,臨時文件缺省存儲在磁盤上,可是該值能夠被temp_store pragma指令覆蓋。
    3). 等於2時,臨時文件缺省存儲在內存中,可是該值能夠被temp_store pragma指令覆蓋。
    4). 等於3時,臨時文件老是存儲在內存中,而不會考慮temp_store pragma指令的設置。
    
     2. 運行時指令temp_store pragma:
    該指令的取值範圍是0到2(缺省值爲0),在程序運行時該指令能夠被動態的設置,見以下說明:
    1). 等於0時,臨時文件的存儲行爲徹底由SQLITE_TEMP_STORE編譯期參數肯定。
    2). 等於1時,若是編譯期參數SQLITE_TEMP_STORE指定使用內存存儲臨時文件,那麼該指令將覆蓋這一行爲,使用磁盤存儲。
    2). 等於2時,若是編譯期參數SQLITE_TEMP_STORE指定使用磁盤存儲臨時文件,那麼該指令將覆蓋這一行爲,使用內存存儲。
    
     3. 臨時文件的大小:
    對於以上兩個參數,都有參數值表示缺省狀況是存儲在內存中的,只有當臨時文件的大小超過必定的閾值後纔會根據必定的算法,將部分數據寫入到磁盤中,以避免臨時文件佔用過多的內存而影響其它程序的執行效率。
    
    最後在從新贅述一遍,SQLITE_TEMP_STORE編譯期參數和temp_store pragma運行時指令只會影響除回滾日誌和主數據庫日誌以外的其它臨時文件的存儲策略。換句話說,回滾日誌和主數據庫日誌將老是將數據寫入磁盤,而不會關注以上兩個參數的值。

4、其它優化策略:

    在SQLite中因爲採用了Page Cache的緩衝優化機制,所以即使臨時文件被指定存儲在磁盤上,也只有當該文件的大小增加到必定的尺寸後纔有可能被SQLite刷新到磁盤文件上,在此以前它們仍將駐留在內存中。這就意味着對於大多數場景,若是臨時表和臨時索引的數據量相對較少,那麼它們是不會被寫到磁盤中的,固然也就不會有IO事件發生。只有當它們增加到內存不能容納的時候纔會被刷新到磁盤文件中的。其中SQLITE_DEFAULT_TEMP_CACHE_SIZE編譯期參數能夠用於指定臨時表和索引在佔用多少Cache Page時才須要被刷新到磁盤文件,該參數的缺省值爲500頁。
相關文章
相關標籤/搜索