SQLite是一個(gè)非常受歡迎的數(shù)據(jù)庫(kù),在數(shù)據(jù)庫(kù)排行榜中已經(jīng)進(jìn)入前十的行列。這主要是因?yàn)樵摂?shù)據(jù)庫(kù)非常小巧,而且可以支持Linux、Windows、iOS和Andriod的主流的操作系統(tǒng)。

SQLite非常簡(jiǎn)單,是一個(gè)進(jìn)程內(nèi)的動(dòng)態(tài)庫(kù)數(shù)據(jù)庫(kù)。其最大的特點(diǎn)是可以支持不同的語言來使用,比如C、C++、Java等等。同時(shí),SQLite還是一個(gè)開源的數(shù)據(jù)庫(kù),也就是開發(fā)者可以根據(jù)自己的需求來修改數(shù)據(jù)的功能特性。
SQLite雖然非常小巧,但功能卻非常豐富,正所謂“麻雀雖小,五臟俱全”。SQLite不僅具備基本的SQL特性,還具備索引、觸發(fā)器、視圖和事務(wù)等特性。
SQLite的主要API
SQLite提供兩種訪問接口,一種是通過sqlite命令行工具,另外一種是通過動(dòng)態(tài)庫(kù),也就是API函數(shù)。在學(xué)習(xí)SQLite架構(gòu)之前,我們有必要對(duì)其API進(jìn)行一個(gè)簡(jiǎn)要的介紹。其實(shí)SQLite的API很簡(jiǎn)單,主要包括三個(gè),分別是sqlite3_open、sqlite3_exec和sqlite3_close三個(gè)函數(shù)。其中sqlite3_exec則是用于執(zhí)行SQL語句的函數(shù)。
也就是說sqlite3_exec是SQLite功能的關(guān)鍵入口,我們后面分析代碼也應(yīng)該以此函數(shù)作為突破點(diǎn)。其它函數(shù)相對(duì)簡(jiǎn)單,也沒那么重要。
SQLite整體架構(gòu)
首先我們從整體架構(gòu)上介紹一下SQLIte。其架構(gòu)如圖所示,包括接口層、SQL命令處理器和存儲(chǔ)后端等。

最為核心的不是就是SQLite內(nèi)核了。其中包括接口層、SQL命令處理器和虛擬機(jī)三部分。SQL命令處理器負(fù)責(zé)對(duì)用戶的SQL進(jìn)行預(yù)處理,最終生成適用于虛擬機(jī)執(zhí)行的代碼。
其下是后端部分,后端部分相當(dāng)于存儲(chǔ)引擎。下面我們簡(jiǎn)要的介紹一下每個(gè)模塊的功能。
(1) 接口
SQLIte庫(kù)的使用通過函數(shù)調(diào)用實(shí)現(xiàn)。為了避免與其它庫(kù)出現(xiàn)沖突,SQLite的函數(shù)都以sqlite3作為前綴。接口部分的實(shí)現(xiàn)在文件main.c,legacy.c和vdbeapi.c中。其中main.c中包含其主要的接口,包括sqlite3_open、sqlite3_config和sqlite3_close等等。SQLite中最終的函數(shù)不在main.c中,而是在legacy.c中,該文件中只包含這一個(gè)接口的實(shí)現(xiàn)。
(2) 詞法分析器
詞法分析器對(duì)SQL語句字符串進(jìn)行解析,最終生成單詞(token)序列。并且將生成的單詞序列傳給解析器進(jìn)行下一步的動(dòng)作。該功能的具體實(shí)現(xiàn)在文件tokenize.c中,核心入口函數(shù)為sqlite3RunParser。
(3) 解析器
SQLite的解析器基于Lemon實(shí)現(xiàn),它實(shí)現(xiàn)將SQL語句字符串解析成語法樹。Lemon是一個(gè)與YACC/BISON類似的詞法分析庫(kù)。該庫(kù)的源代碼在tool目錄中。
(4) 代碼生成器
代碼生成器用于生成與SQL語句對(duì)應(yīng),可以在虛擬機(jī)執(zhí)行的代碼。代碼生成器實(shí)現(xiàn)比較復(fù)雜,包含的文件有:build.c, delete.c, attach.c, expr.c, insert.c, pragma.c, select.c, auth.c等等。通過文件名可以看出,這里很多文件其實(shí)分別對(duì)應(yīng)著一個(gè)SQL語句,比如delete,insert和select等。
(5) 虛擬機(jī)
SQL的具體執(zhí)行在一個(gè)稱為虛擬機(jī)的組件中進(jìn)行的,這個(gè)在前面架構(gòu)圖中已經(jīng)有所展示。虛擬機(jī)執(zhí)行的代碼有前面代碼生成器產(chǎn)生。虛擬機(jī)的實(shí)現(xiàn)在文件vdbe.h和vdbe.c中。
(6) B-樹
SQLite的數(shù)據(jù)通過B樹進(jìn)行組織管理。每個(gè)表或者索引都有一個(gè)對(duì)應(yīng)的B樹。所有的B樹存儲(chǔ)在一個(gè)數(shù)據(jù)庫(kù)文件中。B樹的具體實(shí)現(xiàn)在btree.c和btree.h文件中。
(7) 頁(yè)緩存
SQLite的文件被劃分為等份大小,B樹也是以該大小為粒度來對(duì)數(shù)據(jù)進(jìn)行管理。頁(yè)緩存是該粒度對(duì)應(yīng)的內(nèi)存內(nèi)容,通過該內(nèi)存實(shí)現(xiàn)對(duì)數(shù)據(jù)塊的讀寫等訪問。頁(yè)緩存相關(guān)的實(shí)現(xiàn)在pager.c和pcache.c等文件中。
(8) 操作系統(tǒng)接口
SQLite是一個(gè)跨平臺(tái)的數(shù)據(jù)庫(kù),其存儲(chǔ)數(shù)據(jù)需要兼容Windows和Linux的文件系統(tǒng)API。為了方便,SQLite實(shí)現(xiàn)了一個(gè)抽象層。這樣對(duì)于SQLite業(yè)務(wù)邏輯來說,只需要調(diào)用該抽象層的接口即可,而不用關(guān)心操作系統(tǒng)。
(9) 基礎(chǔ)庫(kù)
包含一個(gè)被各個(gè)模塊都可能使用到的基礎(chǔ)庫(kù),比如內(nèi)存分配,字符串處理等。
SQLite文件格式
前文我們簡(jiǎn)要的介紹了一下SQLite的軟件架構(gòu)以及每個(gè)組件的基本功能。接下來我們介紹一下數(shù)據(jù)庫(kù)文件的相關(guān)功能。
在SQLite中一個(gè)文件承載著一個(gè)數(shù)據(jù)庫(kù)實(shí)例,這個(gè)文件稱為主庫(kù)文件(main database file)。除了主庫(kù)文件外,還可能有一些其它文件,比如用于事務(wù)的日志文件等。本文主要集中介紹主庫(kù)文件,其它文件后續(xù)介紹。
(1) 頁(yè)
數(shù)據(jù)庫(kù)文件由多個(gè)頁(yè)構(gòu)成,每個(gè)頁(yè)的大小在512到65536字節(jié)之間,且大小必須是2的冪。頁(yè)通過編號(hào)進(jìn)行標(biāo)記,起始值為1,最大編號(hào)為2的31次冪-2。頁(yè)的默認(rèn)大小是4KB,本文以默認(rèn)大小為例進(jìn)行介紹。

在數(shù)據(jù)庫(kù)中的每個(gè)頁(yè)都有一個(gè)特定的用途,這些用途包括:
- 鎖字節(jié)頁(yè)(Lock-byte page)
- 剩余 頁(yè)
- B樹 頁(yè)
- 指針映射頁(yè)
- 有效負(fù)載溢出頁(yè)
數(shù)據(jù)庫(kù)文件的第一個(gè)頁(yè)是比較特殊的,它包含整個(gè)數(shù)據(jù)庫(kù)文件的描述信息,這里稱為數(shù)據(jù)庫(kù)頭信息。
(2) 數(shù)據(jù)庫(kù)頭
數(shù)據(jù)庫(kù)頭包含100個(gè)字節(jié)的內(nèi)容,其中每一個(gè)成員的偏移,大小和功能如下圖所示。

我們可以創(chuàng)建一個(gè)數(shù)據(jù)庫(kù)實(shí)例,然后對(duì)照文件內(nèi)容與數(shù)據(jù)庫(kù)頭的格式進(jìn)行理解。比如數(shù)據(jù)庫(kù)頭的第一個(gè)成員為一個(gè)魔數(shù),用于標(biāo)識(shí)該文件為SQLite數(shù)據(jù)庫(kù)文件及版本。在下圖中可以找到該信息,可以看出兩者完全匹配(SQLite format 3)。

除了上述數(shù)據(jù)庫(kù)頭的格式外,每個(gè)不同的頁(yè)都有不同的布局。