寫在前面:SQLite作為嵌入式資料庫,通常針對的應用的資料量相對於通常DBMS的資料量是較小的。所以它的儲存模型設計得非常簡單,總的來說,SQLite把一個資料檔案分成若干大小相等的頁面,然後以B樹的形式來組織這些頁面。而對於大型的資料庫管理系統,比如Oracle,或者DM ,儲存模型要複雜得多。就拿Oracle來說吧,它對資料檔案不僅從物理進行分塊,而且從邏輯上進行分段,盤區和頁的一個層次劃分,DM也一樣。不管怎麼說,資料庫檔案要儲存大量的資料,為了更好管理,查詢和操作資料檔案,DBMS不得不從物理上、邏輯上對資料檔案的資料進行複雜的組織。本節主要討論檔案格式,下節討論頁面格式。
1、檔案格式
1.1、資料庫名稱
應用程式通過sqlite3_open API來開啟資料庫,該函數的一個參數為資料庫檔案的名稱。SQLite內部命名為main資料庫(除了臨時資料庫和記憶體資料庫)。SQLite對每一個資料庫都建立一個獨立的檔案。
在SQLite內部,資料檔案名不是資料庫名。SQLite對應用程式的每一個串連都維護著一個單獨的臨時資料庫(temp資料庫),臨時資料庫存臨時對象,例如:表以及相應的索引。這些臨時對象僅僅對同一個串連可見(對同一個線程,進程的其它串連是不可見的),SQLite儲存臨時資料庫到一個單獨的臨時檔案中,當應用程式關閉對main資料庫的串連時,就刪除臨時檔案。
1.2、資料庫檔案結構
除了記憶體資料庫,SQLite把一個資料庫(main和temp)都儲存到一個單獨的檔案。
1.2.1、頁面(page)
為了更好的管理和讀/寫資料庫,SQLite把一個資料庫(包括記憶體資料庫)分成一個個固定大小的頁面。頁面大小的範圍從512-32768(兩者都包含),頁面預設大小為1024個位元組(1KB),實際上,頁面的上限由2個位元組的有符號整數決定。整個資料庫可以看成這些頁面的數組,頁面數組的下標為頁面的編號(page number),page number從1開始,一直到2,147,483,647 (2^31– 1)。實際上,數組上界還受檔案系統允許的最大檔案大小決定。0號頁面視為空白頁面(NULL page),物理上不存在,1號頁面從檔案的0位移處開始,一個頁面接著下一個頁面。
註:一旦資料庫建立,SQLite使用編譯時間確定的預設的頁面大小。當然,在建立第一個表之前,可以通過pragma命令改變頁面大小。SQLite把該值作為中繼資料的一部分儲存在檔案中。
1.2.2、頁面類型
頁面(page)分四種類型:葉子頁面(leaf),內部頁面(internal),溢出頁面(overflow)和空閑頁面(free)。內部頁麵包含查詢時的導航資訊,葉子頁面儲存資料,例如元組。如果一個元組的資料太大,一個頁面容納不下,則一些資料存放區在B樹的頁面中,餘下的儲存在溢出頁面中。
1.2.3、檔案頭(file header)
作為檔案開始的1號頁面比較特殊,它包括100個位元組的檔案頭。當SQLite建立檔案時例初始設定檔案頭,檔案頭的格式如下:
Structure of database file header |
Offset |
Size |
Description |
0 |
16 |
Header string |
16 |
2 |
Page size in bytes |
18 |
1 |
File format write version |
19 |
1 |
File format read version |
20 |
1 |
Bytes reserved at the end of each page |
21 |
1 |
Max embedded payload fraction |
22 |
1 |
Min embedded payload fraction |
23 |
1 |
Min leaf payload fraction |
24 |
4 |
File change counter |
28 |
4 |
Reserved for future use |
32 |
4 |
First freelist page |
36 |
4 |
Number of freelist pages |
40 |
60 |
15 4-byte meta values |
樣本資料(100個位元組):
53 51 4C 69 74 65 20 66 SQLite f 6F 72 6D 61 74 20 33 00 ormat 3. 04 00 01 01 00 40 20 20 .....@ 00 00 00 11 00 00 00 00 ........ 00 00 00 00 00 00 00 00 ........ 00 00 00 01 00 00 00 01 ........ 00 00 00 00 00 00 00 00 ........ 00 00 00 01 00 00 00 00 ........ 00 00 00 00 00 00 00 00 ........ 00 00 00 00 00 00 00 00 ........ 00 00 00 00 00 00 00 00 ........ 00 00 00 00 00 00 00 00 ........ 00 00 00 00 |
Header string(頭字串):
16個位元組:"SQLite format 3."
Page size:
頁面大小:0x04 00 ,即1024
File format:
檔案格式:0x01 ,0x01,在當前的版本都為1。
Reserved space:
保留空間:0x00,1個位元組,SQLite在每個頁面的末尾都會保留一定的空間,留作它用,預設為0。
Embedded payload:
max embedded payload fraction(位移21)的值限定了B樹內節點(頁面)中一個元組(記錄,單元)最多能夠使用的空間。255意味著100%,預設值為0x40,即64(25%),這保證了一個結點(頁面)至少有4個單元。如果一個單元的負載(payload,即資料量)超過最大值,則溢出的資料儲存到溢出的頁面,一旦SQLite分配了一個溢出頁面,它會儘可能多的移動資料到溢出頁面,下限為min embedded payload fraction value(位移為22),預設的值為32,即12.5% 。
min leaf payload fraction的含義與min embedded payload fraction類似,只不過是它是針對B樹的葉子結點,預設值為32,即12.5%,葉子結點最大的負載為通常是100%,這不用儲存。
File change counter:
檔案修改計數,通常被事務使用,它由事務增加其值。該值的主要目的是資料庫改變時,pager避免對緩衝進行刷盤。
Freelist:
空閑頁面鏈表,在檔案頭位移32的4個位元組記錄著空閑頁面鏈的第一個頁面,位移36處的4個位元組為空白閑頁面的數量。空閑頁面鏈表的組織形式如下:
空閑頁面分為兩種頁面:trunk pages(首頁面)和leaf pages(葉子頁面)。檔案頭的指標指向空閑鏈表的第一個trunk page,每個trunk page指向多個葉子頁面。
Trunk page的格式如下,從頁面的起始處開始:
(1)4個位元組,指向下一個trunk page的頁面號;
(2)4個位元組,該頁面的葉子頁面指標的數量;
(3)指向葉子頁面的頁面號,每項4個位元組。
當一個頁面不再使用時,SQLite把它加入空閑頁面鏈表,並不從本地檔案系統中釋放掉。當添加新的資料到資料庫時,SQLite就從空閑鏈表上取出空閑頁面用來在儲存資料。當空閑鏈表為空白時,SQLite就通過本地檔案系統增加新的頁面,添加到資料庫檔案的末尾。
註:可以通過vacuum命令刪除空閑鏈表,該命令通過把資料庫中資料拷貝到臨時檔案,然後在事務的保護下,用臨時檔案中的複本覆蓋原資料庫檔案。
Meta variables
中繼資料變數:從位移為40開始,為15個4位元組的中繼資料變數,這些中繼資料主要與B樹和VM有關。如下:
** Meta values are as follows: ** meta[0] Schema cookie. Changes with each schema change. ** meta[1] File format of schema layer. ** meta[2] Size of the page cache. ** meta[3] Use freelist if 0. Autovacuum if greater than zero. ** meta[4] Db text encoding. 1:UTF-8 2:UTF-16LE 3:UTF-16BE ** meta[5] The user cookie. Used by the application. ** meta[6] ** meta[7] ** meta[8] ** meta[9] |
1.2.4、讀取檔案頭
當應用程式調用API sqlite3_open開啟資料庫檔案時,SQLite就會讀取檔案頭進行資料庫的初始化。
int sqlite3BtreeOpen(
const char *zFilename, /* Name of the file containing the BTree database */
sqlite3 *pSqlite, /* Associated database handle */
Btree **ppBtree, /* Pointer to new Btree object written here */
int flags /* Options */
){
//讀取檔案頭
sqlite3pager_read_fileheader(pBt->pPager, sizeof(zDbHeader), zDbHeader);
//設定頁面大小
pBt->pageSize = get2byte(&zDbHeader[16]);
//…
}
轉自:http://www.cnblogs.com/hustcat