SQLite體系結構

l_serein發表於2013-03-03

這份文件描述了sqlite資料庫的體系結構,這些資訊對想理解或者修改內部工作原理的朋友們會很有用。


 

    上圖中包含了sqlite的主要的模組以及它們是怎麼關聯的。下面就簡要介紹一下每個模組。
  (這份文件描述的是sqlite 3.0版本的,2.8以及以前的大體相似只是細節上有點不同)

 

介面
     大部分sqlite的介面在main.c,legacy.c,vdbeapi.c原始檔中實現,但是有些常規性的分散在其他的檔案中,因為它們可以在檔案作用域內訪問一些資料結構。比如,sqlite3_get_table() 在table.c中實現,sqlite3_mprintf() 在printf.c中實現,sqlite3_complete()在tokenizer.c中實現,tcl介面在tclsqlite.c中實現,更多關於sqlite的c介面可以訪問
     為了避免與其他的軟體產生命名衝突,所有的對外符號都以sqlite3作為字首。(也就是說,這些符號構成了sqlite的api)

 

分詞器
     當一個包含sql語句的字串執行的時候,介面把字串傳遞給分詞器。分詞器的工作就是把原始的字串分割成標記,然後一個一個的傳給語法分析器。這個分詞器在tokenizer.c中用c實現。

     注意在這個設計裡面,分詞器呼叫語法分析器。那些熟悉YACC或者BISON的朋友們習慣用另一種方式設計-----用語法分析器呼叫分詞器。sqlite的作者分別用這兩種方法實現過,最後發現第一種方法工作效率比較高。YACC有很多侷限性。

 

語法分析器
     語法分析器是一個根據環境給符號賦予意義的模組。這個分析器用了lemo的LALR(1)分析器生成。lemon跟YACC/BISON差不多,但是它用了一種很少產生錯誤的輸入句法(syntax),而且lemon可以產生一個可重入(reentrant)和執行緒安全(thread-safe)的分析器。lemon可以定義無端點的破壞(non-terminal destructor)的概念,所以當出現錯誤句法時不會出現記憶體洩露。
     由於lemon是一個一般不會經常使用的程式,lemon完整的原始檔(就一個c檔案)在sqlite釋出版本的tool資料夾下。關於lemon的文件在doc資料夾下。

 

程式碼生成器
      在分析器把符號翻譯(assemble)成完整的sql語句的後,它會呼叫程式碼生成器產生sql語句需要執行的虛擬機器程式碼。程式碼生成器包含很多檔案,attach.c,auth.c,build.c,delete.c,expr.c,insert.c,pragma.c,select.c,trigger.c update.c,vacuum.c和where.c。這些檔案是大部分古怪魔法(serious magic)發生的地方。expr.c處理表示式的程式碼生成,where.c 處理select,update,delete語句中where子句的程式碼生成,attach.c,delete.c insert.c select.c trigger.c update.c 和 vacuum.c 處理跟它們名字有關語句的程式碼生成(當有必要時,這些檔案呼叫expr.c和where.c中常用的函式)。其餘的sql 語句在build.c中生成。auth.c檔案實現sqlite3_set_authorizer()介面.
  
虛擬機器
   由程式碼生成器生成的程式碼被虛擬機器執行。更多的關於虛擬的的資訊可以訪問————。摘要而言,虛擬機器實現了一個專門處理資料庫檔案的抽象的計算引擎(abstract computing engine)。這個虛擬機器包含了一個作為中間儲存器的棧,每條指令包含一個操作碼和多達3個的運算元。
    這個虛擬機器完全在vdbe.c中實現,它有自己的標頭檔案vdbe.h,裡面定義了虛擬機器和其他的模組的介面。vdbeInt.h裡面定義了虛擬機器裡面私有的資料結構,vdbeaux.c裡面包含了虛擬機器常用的工具,也包含了其餘為構造虛擬機器程式的庫的介面模組。vdbeapi.c 包含了虛擬機器對外的介面比如sqlite3_bind_... 的函式族。資料型別(individual values)(strings,integer,floating point numbers,和BLOBs)儲存在一個內部的叫做“mem”物件當中,在vdbemen.c中。
    sqlite用回撥c語言函式(c-language routines)的方法實現sql 函式。甚至內建的sql函式都是用這種方式實現的。很多內建的sql函式(比如coalesce(),count(),substr(),還有很多),在func.c中實現,資料和時間的轉換函式在date.c中實現。

 

B-Tree
   sqlite資料庫在硬碟上用B-tree實現,具體程式碼在btree.c中。在資料庫中的每個表和索引都使用單獨的b-tree。所有的b-tree儲存在用一個檔案中。檔案格式的細節在btree.c開頭的一大段註釋中記錄。
   b-tree的介面在btree.h標頭檔案中記錄。

 

頁快取
  B-tree模組需要以一個固定大小的塊從硬碟讀取資料。預設的塊大小是1024個位元組,可以在512和65536個位元組間變化。頁快取的任務是讀,寫,快取塊。頁快取提供回滾,原子委託抽象(atomic commit abstraction),處理資料檔案的鎖。b-tree需要從頁快取請求特殊的頁,當修改頁,委託,回滾時通知頁快取,頁快取處理所以的細節以確定快速,安全,高效的處理請求。

  實現頁快取的程式碼在pager.c中,頁快取子系統的介面在pager.h標頭檔案中定義。

 

作業系統介面
   為了在POSIX和win32作業系統之間移植,sqlite用了一個抽象的層作為與作業系統間的介面。實現OS抽象層的介面在os.h中定義,每一種支援的系統有它們自己的實現方式,os_unix.c為unix,os_win.c為windows,當然也有自己相應的標頭檔案os_unix.h和os_win.h,等等。

工具
  記憶體配置和字串比較的功能在util.c中,語法分析器用到的符號表用hash 表實現,具體程式碼在hash.c中。utf.c包含了Unicode轉換子程式。sqlite有它自己printf()實現(增加了很多功能),在printf.c中。也有自己的隨機數生成的實現,在random.c中。
   
測試程式碼
    如果你依賴測試指令碼,一多半sqlite的程式碼是為了測試用的。很多assert()語句在主程式碼檔案中。另外,從test1.c到test5.c和md5.c只是為測試實現的,os_test.c中的介面是為了模擬斷電時,核實頁中錯誤恢復機制的。

相關文章