寫在前面:本節是前一節內容的後續部分,這兩節都是從全域的角度SQLite核心各個模組的設計和功能。只有從全域上把握SQLite,才會更容易的理解SQLite的實現。SQLite採用了層次化,模組化的設計,而這些使得它的可擴充性和可移植性非常強。而且SQLite的架構與通用DBMS的結構差別不是很大,所以它對於理解通用DBMS具有重要意義。好了,下面我們開始討論SQLite剩餘的兩部分:Back-end(後端)和compiler(編譯器)。
2、B-tree和Pager
B-Tree使得VDBE可以在O(logN)下查詢,插入和刪除資料,以及O(1)下雙向遍曆結果集。B-Tree不會直接讀寫磁碟,它僅僅維護著頁面(pages)之間的關係。當B-TREE需要頁面或者修改頁面時,它就會調用Pager。當修改頁面時,pager保證原始頁面首先寫入記錄檔,當它完成寫操作時,pager根據事務狀態決定如何做。B-tree不直接讀寫檔案,而是通過page cache這個緩衝模組讀寫檔案對於效能是有重要意義的(註:這和作業系統讀寫檔案類似,在Linux中,作業系統的上層模組並不直接調用裝置驅動讀寫裝置,而是通過一個高速緩衝模組調用裝置驅動讀寫檔案,並將結果存到高速緩衝區)。
2.1、資料庫檔案格式(Database File Format)
資料庫中所有的頁面都按從1開始順序標記。一個資料庫由許多B-tree構成——每一個表和索引都有一個B-tree(註:索引採用B-tree,而表採用B+tree,這主要是表和索引的需求不同以及B-tree和B+tree的結構不同決定的:B+tree的所有葉子節點包含了全部關鍵字資訊,而且可以有兩種順序尋找——具體參見《資料結構》,嚴蔚敏。而B-tree更適合用來作索引)。所有表和索引的根頁面都儲存在sqlite_master表中。
資料庫中第一個頁面(page 1)有點特殊,page 1的前100個位元組包含一個描述資料庫檔案的特殊的檔案頭。它包括庫的版本,模式的版本,頁面大小,編碼等所有建立資料庫時設定的參數。這個特殊的檔案頭的內容在btree.c中定義,page 1也是sqlite_master表的根頁面。
2.1、頁面重用及回收(Page Reuse and Vacuum )
SQLite利用一個空閑列表(free list)進行頁面回收。當一個頁面的所有記錄都被刪除時,就被插入到該列表。當運行VACUUM命令時,會清除free list,所以資料庫會縮小,本質上它是在新的檔案重建立立資料庫,而所有使用的頁在都被拷貝過去,而free list卻不會,結果就是一個新的,變小的資料庫。當資料庫的autovacuum開啟時,SQLite不會使用free list,而且在每一次commit時自動壓縮資料庫。
2.2、B-Tree記錄
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太大不能存在一個頁面內進便儲存到多個頁面。
B+Tree按關鍵字排序,所有的關鍵字必須唯一。表採用B+tree,內部頁面不包含資料,如下:
B+tree中根頁面(root page)和內部頁面(internal pages)都是用來導航的,這些頁面的資料域都是指向下級頁面的指標,僅僅包含關鍵字。所有的資料庫記錄都儲存在葉子頁面(leaf pages)內。在分葉節點一級,記錄和頁面都是按照關鍵字的順序的,所以B-tree可以水平方向遍曆,時間複雜度為O(1)。
2.3、記錄和域(Records and Fields)
位於分葉節點頁面的資料域的記錄由VDBE管理,資料庫記錄以二進位的形式儲存,但有一定的資料格式。記錄格式包括一個邏輯頭(logical header)和一個資料區(data segment),header segment包括header的大小和一個資料類型數組,資料類型用來在data segment的資料的類型,如下:
2.4、層次資料群組織(Hierarchical Data Organization)
從上往下,資料越來越無序,從下向上,資料越來越結構化.
2.5、B-Tree API
B-Tree模組有它自己的API,它可以獨立於C API使用。另一個特點就是它支援事務。由pager處理的事務,鎖和日誌都是為B-tree服務的。根據功能可以分為以下幾類:
2.5.1、訪問和事務函數
sqlite3BtreeOpen: Opens a new database file. Returns a B-tree object.
sqlite3BtreeClose: Closes a database.
sqlite3BtreeBeginTrans: Starts a new transaction.
sqlite3BtreeCommit: Commits the current transaction.
sqlite3BtreeRollback: Rolls back the current transaction.
sqlite3BtreeBeginStmt: Starts a statement transaction.
sqlite3BtreeCommitStmt: Commits a statement transaction.
sqlite3BtreeRollbackStmt: Rolls back a statement transaction.
2.5.2、表函數
sqlite3BtreeCreateTable: Creates a new, empty B-tree in a database file.
sqlite3BtreeDropTable: Destroys a B-tree in a database file.
sqlite3BtreeClearTable: Removes all data from a B-tree, but keeps the B-tree intact.
2.5.3、遊標函數(Cursor Functions)
sqlite3BtreeCursor: Creates a new cursor pointing to a particular B-tree.
sqlite3BtreeCloseCursor: Closes the B-tree cursor.
sqlite3BtreeFirst: Moves the cursor to the first element in a B-tree.
sqlite3BtreeLast: Moves the cursor to the last element in a B-tree.
sqlite3BtreeNext: Moves the cursor to the next element after the one it is currently
pointing to.
sqlite3BtreePrevious: Moves the cursor to the previous element before the one it is
currently pointing to.
sqlite3BtreeMoveto: Moves the cursor to an element that matches the key value passed in as a parameter.
2.5.4、記錄函數(Record Functions)
sqlite3BtreeDelete: Deletes the record that the cursor is pointing to.
sqlite3BtreeInsert: Inserts a new element in the appropriate place of the B-tree.
sqlite3BtreeKeySize: Returns the number of bytes in the key of the record that the
cursor is pointing to.
sqlite3BtreeKey: Returns the key of the record the cursor is currently pointing to.
sqlite3BtreeDataSize: Returns the number of bytes in the data record that the cursor is
currently pointing to.
sqlite3BtreeData: Returns the data in the record the cursor is currently pointing to.
2.5.5、配置函數(Configuration Functions)
sqlite3BtreeSetCacheSize: Controls the page cache size as well as the synchronous
writes (as defined in the synchronous pragma).
sqlite3BtreeSetSafetyLevel: Changes the way data is synced to disk in order to increase
or decrease how well the database resists damage due to OS crashes and power failures.
Level 1 is the same as asynchronous (no syncs() occur and there is a high probability of
damage). This is the equivalent to pragma synchronous=OFF. Level 2 is the default. There
is a very low but non-zero probability of damage. This is the equivalent to pragma
synchronous=NORMAL. Level 3 reduces the probability of damage to near zero but with a
write performance reduction. This is the equivalent to pragma synchronous=FULL.
sqlite3BtreeSetPageSize: Sets the database page size.
sqlite3BtreeGetPageSize: Returns the database page size.
sqlite3BtreeSetAutoVacuum: Sets the autovacuum property of the database.
sqlite3BtreeGetAutoVacuum: Returns whether the database uses autovacuum.
sqlite3BtreeSetBusyHandler: Sets the busy handler
2.6、執行個體分析
最後以sqlite3_open的具體實現結束本節的討論(參見Version 3.6.10的源碼):
由可以知道,SQLite的所有IO操作,最終都轉化為作業系統的系統調用(一名話:DBMS建立在痛苦的OS之上)。同時也可以看到SQLite的實現非常的層次化,模組化,使得SQLite更易擴充,可移植性非常強。
3、編譯器(Compiler)
3.1、分詞器(Tokenizer)
介面把要執行的SQL語句傳遞給Tokenizer,Tokenizer按照SQL的詞法定義把它切分一個一個的詞,並傳遞給分析器(Parser)進行文法分析。分詞器是手工寫的,主要在Tokenizer.c中實現。
3.2、分析器(Parser)
SQLite的文法分析器是用Lemon——一個開源的LALR(1)文法分析器的產生器,產生的檔案為parser.c。
一個簡單的文法樹:
SELECT rowid, name, season FROM episodes WHERE rowid=1 LIMIT 1
3.3、代碼產生器(Code Generator)
代碼產生器是SQLite中取龐大,最複雜的部分。它與Parser關係緊密,根據文法分析樹產生VDBE程式執行SQL語句的功能。由諸多檔案構成:select.c,update.c,insert.c,delete.c,trigger.c,where.c等檔案。這些檔案產生相應的VDBE程式指令,比如SELECT語句就由select.c產生。下面是一個讀操作中開啟表的代碼的產生實現:
/* Generate code that will open a table for reading.
*/
void sqlite3OpenTableForReading(
Vdbe *v, /* Generate code into this VDBE */
int iCur, /* The cursor number of the table */
Table *pTab /* The table to be opened */
){
sqlite3VdbeAddOp(v, OP_Integer, pTab->iDb, 0);
sqlite3VdbeAddOp(v, OP_OpenRead, iCur, pTab->tnum);
VdbeComment((v, "# %s", pTab->zName));
sqlite3VdbeAddOp(v, OP_SetNumColumns, iCur, pTab->nCol);
}
Sqlite3vdbeAddOp函數有三個參數:(1)VDBE執行個體(它將添加指令),(2)作業碼(一條指令),(3)兩個運算元。
3.4、查詢最佳化
代碼產生器不僅負責產生代碼,也負責進行查詢最佳化。主要的實現位於where.c中,產生的WHERE語句塊通常被其它模組共用,比如select.c,update.c以及delete.c。這些模組調用sqlite3WhereBegin()開始WHERE語句塊的指令產生,然後加入它們自己的VDBE代碼返回,最後調用sqlite3WhereEnd()結束指令產生,如下: