<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                合規國際互聯網加速 OSASE為企業客戶提供高速穩定SD-WAN國際加速解決方案。 廣告
                [TOC] ## 架構 SQLite采用了模塊的設計,它由三個子系統,包括8個獨立的模塊構成。 ![](https://img.kancloud.cn/44/9c/449cd7a5b939cad3e12089dd42a7253a_634x411.png) ### 接口(Interface) 接口由SQLite C API組成,也就是說不管是程序、腳本語言還是庫文件,最終都是通過它與SQLite交互的(我們通常用得較多的ODBC/JDBC最后也會轉化為相應C API的調用)。 ### 編譯器(Compiler) 在編譯器中,分詞器(Tokenizer)和分析器(Parser)對SQL進行語法檢查,然后把它轉化為底層能更方便處理的分層的數據結構---語法樹,然后把語法樹傳給代碼生成器(code generator)進行處理。而代碼生成器根據它生成一種針對SQLite的匯編代碼,最后由虛擬機(Virtual Machine)執行。 ### 虛擬機(Virtual Machine) 架構中最核心的部分是虛擬機,或者叫做虛擬數據庫引擎(Virtual Database Engine,VDBE)。它和Java虛擬機相似,解釋執行字節代碼。VDBE的字節代碼由128個操作碼(opcodes)構成,它們主要集中在數據庫操作。它的每一條指令都用來完成特定的數據庫操作(比如打開一個表的游標)或者為這些操作棧空間的準備(比如壓入參數)。總之,所有的這些指令都是為了滿足SQL命令的要求(關于VM,后面會做詳細介紹)。 ### 后端(Back-End) 后端由B-樹(B-tree),頁緩存(page cache,pager)和操作系統接口(即系統調用)構成。B-tree和page cache共同對數據進行管理。B-tree的主要功能就是索引,它維護著各個頁面之間的復雜的關系,便于快速找到所需數據。而pager的主要作用就是通過OS接口在B-tree和Disk之間傳遞頁面。 SQLite由很多部分組成-parser,tokenize,virtual machine等等。但是從程序員的角度,最需要知道的是:connection, statements, B-tree和pager ## API層 由兩部分組成: 核心API(core API) 和擴展API(extension API) 核心API的函數實現基本的數據庫操作:連接數據庫,處理SQL,遍歷結果集。它也包括一些實用函數,比如字符串轉換,操作控制,調試和錯誤處理。 擴展API通過創建你自定義的SQL函數去擴展SQLite。 ![](https://uploader.shimo.im/f/i17E2UeR2cYSV1Bu.png!thumbnail) ### Connection 一個連接(Connection)代表在一個獨立的事務環境下的一個連接A ### Statements 每一個statement都和一個connection關聯,它通常表示一個編譯過的SQL語句,在內部,它以VDBE字節碼表示。Statement包括執行一個命令所需要一切,包括保存VDBE程序執行狀態所需的資源,指向硬盤記錄的B-樹游標,以及參數等等。 ### Transaction 一個連接(connection)可以包含多個(statement),而且每個連接有一個與數據庫關聯的B-tree和一個pager。Pager在連接中起著很重要的作用,因為它管理事務、鎖、內存緩存以及負責崩潰恢復(crash recovery)。當你進行數據庫寫操作時,記住最重要的一件事:在任何時候,只在一個事務下執行一個連接。 一個事務的生命和statement差不多,你也可以手動結束它。默認情況下,事務自動提交,當然你也可以通過BEGIN..COMMIT手動提交。接下來就是鎖的問題。 ### 鎖的狀態 ![](https://img.kancloud.cn/09/39/0939fbf9b57a15ee5d2b2f625f336c89_412x464.png) 關于這個圖有以下幾點值得注意: 一個事務可以在UNLOCKED,RESERVED或EXCLUSIVE三種狀態下開始。默認情況下在UNLOCKED時開始。 白色框中的UNLOCKED, PENDING, SHARED和 RESERVED可以在一個數據庫的同一時存在。 從灰色的PENDING開始,事情就變得嚴格起來,意味著事務想得到排斥鎖(EXCLUSIVE)(注意與白色框中的區別)。 //1. 【讀】會獲取到SHARED鎖 ;【寫page】會獲取到RESERVED鎖 //2. SQLite可以高效的處理在同一時刻的多個讀連接和一個寫連接。 //3. page要寫入數據庫文件時,會先去獲取EXCLUSIVE鎖(排斥鎖) 雖然鎖有這么多狀態,但是從體質上來說,只有兩種情況:讀事務和寫事務。 #### 讀事務 ~~~ db = open('foods.db') db.exec('BEGIN') db.exec('SELECT * FROM episodes') db.exec('SELECT * FROM episodes') db.exec('COMMIT') db.close() ~~~ 由于顯式的使用了BEGIN和COMMIT,兩個SELECT命令在一個事務下執行。第一個exec()執行時,connection處于SHARED,然后第二個exec()執行,當事務提交時,connection又從SHARED回到UNLOCKED狀態,如下: UNLOCKED→PENDING→SHARED→UNLOCKED 如果沒有BEGIN和COMMIT兩行時如下: UNLOCKED→PENDING→SHARED→UNLOCKED→PENDING→ SHARED→UNLOCKED ## 后端 ![](https://img.kancloud.cn/88/ad/88ad3df788cf4ed25f0a7ec237e8274a_400x480.png) ### B-tree B-Tree使得VDBE可以在**O(logN)**下查詢,插入和刪除數據,以及O(1)下雙向遍歷結果集。B-Tree不會直接讀寫磁盤,它僅僅維護著頁面(pages)之間的關系。當B-TREE需要頁面或者修改頁面時,它就會調用Pager。當修改頁面時,pager保證原始頁面首先寫入日志文件,當它完成寫操作時,pager根據事務狀態決定如何做。B-tree不直接讀寫文件,而是通過page cache這個緩沖模塊讀寫文件對于性能是有重要意義的。 B-tree中頁面由B-tree記錄組成,也叫做payloads。每一個B-tree記錄,或者payload有兩個域:**關鍵字域(key field)和數據域(data field)**。**Key field就是ROWID的值,或者數據庫中表的關鍵字的值。從B-tree的角度,data field可以是任何無結構的數據。**數據庫的記錄就保存在這些data fields中。B-tree的任務就是排序和遍歷,它最需要就是關鍵字。Payloads的大小是不定的,這與內部的關鍵字和數據域有關,當一個payload太大不能存在一個頁面內進便保存到多個頁面。 ### Page Cache事務處理 pager層是SQLite實現最為核心的模塊,它具有四大功能:I/O,頁面緩存,并發控制和日志恢復。而這些功能不僅是上層Btree的基礎,而且對系統的性能和健壯性有關至關重要的影響。其中并發控制和日志恢復是事務處理實現的基礎。SQLite并發控制的機制非常簡單——封鎖機制;別外,它的查詢優化機制也非常簡單——基于索引。 #### 初始狀態 當一個數據庫連接第一次打開時,狀態如圖所示。圖中最右邊(“Disk”標注)表示保存在存儲設備中的內容。每個方框代表一個扇區。藍色的塊表示這個扇區保存了原始數據。圖中中間區域是操作系統的磁盤緩沖區。開始的時候,這些緩存是還沒有被使用,因此這些方框是空白的。圖中左邊區域顯示SQLite用戶進程的內存。因為這個數據庫連接剛剛打開,所以還沒有任何數據記錄被讀入,所以這些內存也是空的。 ![](https://img.kancloud.cn/2c/01/2c012bb6ec6ab8dceebf4d11389155d3_295x270.png) #### 獲取讀鎖 在SQLite寫數據庫之前,它必須先從數據庫中讀取相關信息。比如,在插入新的數據時,SQLite會先從sqlite\_master表中讀取數據庫模式(相當于數據字典),以便編譯器對INSERT語句進行分析,確定數據插入的位置。 在進行讀操作之前,必須先獲取數據庫的共享鎖(shared lock),共享鎖允許兩個或更多的連接在同一時刻讀取數據庫。但是共享鎖不允許其它連接對數據庫進行寫操作 shared lock存在于操作系統磁盤緩存,而不是磁盤本身。文件鎖的本質只是操作系統的內核數據結構,當操作系統崩潰或掉電時,這些內核數據也會隨之消失。 ![](https://img.kancloud.cn/4f/32/4f32d64b6242303b1839b98726d9b7fe_295x270.png) #### 讀取數據 一旦得到shared lock,就可以進行讀操作。如圖所示,數據先由OS從磁盤讀取到OS緩存,然后再由OS移到用戶進程空間。一般來說,數據庫文件分為很多頁,而一次讀操作只讀取一小部分頁面。如圖,從8個頁面讀取3個頁面 ![](https://img.kancloud.cn/c8/5a/c85a5ca51545c522e7e7ec7acbf8b2ee_295x270.png) #### 獲取Reserved Lock 在對數據進行修改操作之前,先要獲取數據庫文件的Reserved Lock,Reserved Lock和shared lock的相似之處在于,它們都允許其它進程對數據庫文件進行讀操作。Reserved Lock和Shared Lock可以共存,但是只能是一個Reserved Lock和多個Shared Lock——多個Reserved Lock不能共存。所以,在同一時刻,只能進行一個寫操作。 Reserved Lock意味著當前進程(連接)想修改數據庫文件,但是還沒開始修改操作,所以其它的進程可以讀數據庫,但不能寫數據庫 ![](https://img.kancloud.cn/33/03/3303b594ebbabdc9388cbc88d421771f_295x270.png) #### 創建恢復日志(Creating A Rollback Journal File) 在對數據庫進行寫操作之前,SQLite先要創建一個單獨的日志文件,然后把要修改的頁面的原始數據寫入日志。回滾日志包含一個日志頭(圖中的綠色)——記錄數據庫文件的原始大小。所以即使數據庫文件大小改變了,我們仍知道數據庫的原始大小。 從OS的角度來看,當一個文件創建時,大多數OS(Windows,Linux,Mac OS X)不會向磁盤寫入數據,新創建的文件此時位于磁盤緩存中,之后才會真正寫入磁盤。如圖,日志文件位于OS磁盤緩存中,而不是位于磁盤。 ![](https://img.kancloud.cn/58/99/5899426caf62b92ec53e26ff0adc9318_295x351.png) #### 修改位于用戶進程空間的頁面(Changing Database Pages In User Space) 頁面的原始數據寫入日志之后,就可以修改頁面了——位于用戶進程空間。每個數據庫連接都有自己私有的空間,所以頁面的變化只對該連接可見,而對其它連接的數據仍然是磁盤緩存中的數據。從這里可以明白一件事:一個進程在修改頁面數據的同時,其它進程可以繼續進行讀操作。圖中的紅色表示修改的頁面。 ![](https://img.kancloud.cn/c1/58/c158967dbb2cc5453a8e4ea7dfa7c1a7_295x351.png) #### 日志文件刷入磁盤(Flushing The Rollback Journal File To Mass Storage) 接下來把日志文件的內容刷入磁盤,這對于數據庫從意外中恢復來說是至關重要的一步。而且這通常也是一個耗時的操作,因為磁盤I/O速度很慢。 這個步驟不只把日志文件刷入磁盤那么簡單,它的實現實際上分成兩步:首先把日志文件的內容刷入磁盤(即頁面數據);然后把日志文件中頁面的數目寫入日志文件頭,再把header刷入磁盤(這一過程在代碼中清晰可見)。 ![](https://img.kancloud.cn/85/0e/850e0fe58145fdfcf69d25c2beabe046_295x351.png) #### 獲取排斥鎖(Obtaining An Exclusive Lock) 在對數據庫文件進行修改之前(注:這里不是內存中的頁面),我們必須得到數據庫文件的排斥鎖(Exclusive Lock)。得到排斥鎖的過程可分為兩步:首先得到Pending lock;然后Pending lock升級到exclusive lock。 Pending lock允許其它已經存在的Shared lock繼續讀數據庫文件,但是不允許產生新的shared lock,這樣做目的是為了防止寫操作發生餓死情況。一旦所有的shared lock完成操作,則pending lock升級到exclusive lock。 ![](https://img.kancloud.cn/8b/73/8b739dd8c467ef5852774c1673fb246e_295x351.png) #### 修改的頁面寫入文件(Writing Changes To The Database File) 一旦得到exclusive lock,其它的進程就不能進行讀操作,此時就可以把修改的頁面寫回數據庫文件,但是通常OS都把結果暫時保存到磁盤緩存中,直到某個時刻才會真正把結果寫入磁盤。 ![](https://img.kancloud.cn/ee/70/ee70e34429bb31553bdf8a03a5e1a2c5_295x351.png) #### 修改結果刷入存儲設備(Flushing Changes To Mass Storage) 為了保證修改結果真正寫入磁盤,這一步必不要少。對于數據庫存的完整性,這一步也是關鍵的一步。由于要進行實際的I/O操作,所以和第7步一樣,將花費較多的時間 ![](https://img.kancloud.cn/ee/70/ee70e34429bb31553bdf8a03a5e1a2c5_295x351.png) #### 刪除日志文件(Deleting The Rollback Journal) 一旦更改寫入設備,日志文件將會被刪除,這是事務真正提交的時刻。如果在這之前系統發生崩潰,就會進行恢復處理,使得數據庫和沒發生改變一樣;如果在這之后系統發生崩潰,表明所有的更改都已經寫入磁盤。SQLite就是根據日志存在情況決定是否對數據庫進行恢復處理。 刪除文件本質上不是一個原子操作,但是從用戶進程的角度來看是一個原子操作,所以一個事務看起來是一個原子操作。 在許多系統中,刪除文件也是一個高代價的操作。作為優化,SQLite可以配置成把日志文件的長度截為0或者把日志文件頭清零。 ![](https://img.kancloud.cn/45/ad/45adb56e0a9ba22b7316f84cd6b8f791_295x351.png) #### 釋放鎖(Releasing The Lock) 作為原子提交的最后一步,釋放排斥鎖使得其它進程可以開始訪問數據庫了。 ![](https://img.kancloud.cn/fa/5c/fa5caaef4ee1f9f864301394c9410287_295x270.png) ## 參考資料 [深入理解sqlite](http://www.hmoore.net/kangdandan/sqlite/64352)
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看