1. 程式人生 > >SQLite學習筆記(15)-B-tree(1)

SQLite學習筆記(15)-B-tree(1)

B-Tree

B-tree的主要功能就是索引,它維護著各個頁面之間的複雜的關係,便於快速找到所需資料。

B-Tree使得VDBE可以在O(logN)下查詢,插入和刪除資料,以及O(1)下雙向遍歷結果集。B-Tree不會直接讀寫磁碟,它僅僅維護著頁面 (pages)之間的關係。當B-TREE需要頁面或者修改頁面時,它就會呼叫Pager。當修改頁面時,pager保證原始頁面首先寫入日誌檔案,當它完成寫操作時,pager根據事務狀態決定如何做。B-tree不直接讀寫檔案,而是通過page cache這個緩衝模組讀寫檔案對於效能是有重要意義的。

2.5.1 資料庫檔案格式(DatabaseFile Format)

從邏輯上來說,一個SQLite資料庫檔案由多個多重Btree構成。一個數據庫由許多B-tree構成——每一個表和索引都有一個B-tree(注:索引採用B-tree,而表採用B+tree,這主要是表和索引的需求不同以及B-tree和B+tree的結構不同決定的:B+tree的所有葉子節點包含了全部關鍵字資訊,而且可以有兩種順序查詢。而B-tree更適合用來作索引)。

2.5.1.1 頁

資料庫檔案包括一個或多個頁。頁的大小可以是512B到64KB之間的2的任意次方。

資料庫中所有的頁面都按從1開始順序標記,其最大頁數目為2147483646 (  - 2)[6]。資料庫中的頁主要分為以下幾類:

  • The lock-byte page
  • A freelist page
    • A freelist trunk page
    • A freelist leaf page
  • A b-tree page
    • A table b-tree interior page
    • A table b-tree leaf page
    • An index b-tree interior page
    • An index b-tree leaf page
  • A payload overflow page
  • A pointer map page

資料庫中載入到記憶體中的頁的定義位於btreeInt.h,其結構如下:

<pre name="code" class="cpp">structMemPage {
  u8 isInit;           /* True if previously initialized.MUST BE FIRST! */
  u8 nOverflow;        /* Number of overflow cell bodies inaCell[] */
  u8 intKey;           /* True if table b-trees.  False for index b-trees */
  u8 intKeyLeaf;       /* True if the leaf of an intKey table*/
  u8 noPayload;        /* True if internal intKey page (thusw/o data) */
  u8 leaf;             /* True if a leaf page */
  u8 hdrOffset;        /* 100 for page 1.  0 otherwise */
  u8 childPtrSize;     /* 0 if leaf==1.  4 if leaf==0 */
  u8 max1bytePayload;  /* min(maxLocal,127) */
  u8 bBusy;            /* Prevent endless loops on corruptdatabase files */
  u16 maxLocal;        /* Copy of BtShared.maxLocal orBtShared.maxLeaf */
  u16 minLocal;        /* Copy of BtShared.minLocal orBtShared.minLeaf */
  u16 cellOffset;      /* Index in aData of first cell pointer*/
  u16 nFree;           /* Number of free bytes on the page*/
  u16 nCell;           /* Number of cells on this page,local and ovfl */
  u16 maskPage;        /* Mask for page offset */
  u16 aiOvfl[5];       /* Insert the i-th overflow cell beforethe aiOvfl-th
                       ** non-overflow cell */
  u8 *apOvfl[5];       /* Pointers to the body of overflowcells */
  BtShared *pBt;       /* Pointer to BtShared that this page ispart of */
  u8 *aData;           /* Pointer to disk image of the page data */
  u8 *aDataEnd;        /* One byte past the end of usable data*/
  u8 *aCellIdx;        /* The cell index area */
  u8 *aDataOfst;       /* Same as aData for leaves.  aData+4 for interior */
  DbPage *pDbPage;     /* Pager page handle */
  u16 (*xCellSize)(MemPage*,u8*);             /* cellSizePtr method */
  void (*xParseCell)(MemPage*,u8*,CellInfo*);/* btreeParseCell method */
  Pgno pgno;           /* Page number for this page */
};

2.5.1.2 檔案頭

資料庫中第一個頁(page 1)永遠是Btree頁。而每個表或索引的第1個頁稱為根頁,所有表或索引的根頁編號都儲存在系統表sqlite_master中,表sqlite_master的根頁為page 1。Page 1的前100個位元組是一個對資料庫檔案進行描述的“檔案頭”。它包括資料庫的版本、格式的版本、頁大小、編碼等所有建立資料庫時設定的永久性引數。這個特殊檔案頭的文件在btreeInt.h中,格式如表2.2所示。

表2.2 Page 1的前100位元組

偏移量

大小

說明

0

16

The header string: "SQLite format 3\000"

16

2

The database page size in bytes. Must be a power of two between 512 and 65536.

18

1

File format write version. 1 for legacy; 2 for WAL.

19

1

File format read version. 1 for legacy; 2 for WAL.

20

1

Bytes of unused "reserved" space at the end of each page. Usually 0.

21

1

Maximum embedded payload fraction. Must be 64.

22

1

Minimum embedded payload fraction. Must be 32.

23

1

Leaf payload fraction. Must be 32.

24

4

File change counter.

28

4

Size of the database file in pages. The "in-header database size".

32

4

Page number of the first freelist trunk page.

36

4

Total number of freelist pages.

40

4

The schema cookie.

44

4

The schema format number. Supported schema formats are 1, 2, 3, and 4.

48

4

Default page cache size.

52

4

The page number of the largest root b-tree page when in auto-vacuum or incremental-vacuum modes, or zero otherwise.

56

4

The database text encoding. A value of 1 means UTF-8. A value of 2 means UTF-16le. A value of 3 means UTF-16be.

60

4

The "user version" as read and set by the user_version pragma.

64

4

True (non-zero) for incremental-vacuum mode. False (zero) otherwise.

72

20

Reserved for expansion. Must be zero.

2.5.1.3 鎖位元組頁(TheLock-Byte Page)

鎖位元組頁是資料庫檔案中的單獨一頁,它包含偏移量在1073741824和1073742335位元組之間的位元組。不大於1073741824位元組的資料庫檔案沒有鎖位元組頁。只有大於1073741824位元組的資料庫檔案含有一頁鎖位元組頁。

SQLite不使用鎖位元組頁,它是專門留給OS在VFS實現中的資料庫檔案鎖定原語。

鎖位元組頁定義在btreeInt.h,其結構如下:

structBtLock {
  Btree *pBtree;        /* Btree handle holding this lock */
  Pgno iTable;          /* Root page of table */
  u8 eLock;             /* READ_LOCK or WRITE_LOCK *
  BtLock *pNext;        /* Next in BtShared.pLock list */
};

2.5.1.4 空閒頁連結串列(TheFreelist)

一個數據庫檔案或多或少含有一些沒有使用的頁。這些頁可能是由於其中的資訊被資料庫刪除產生的。這些空閒頁被存放在空閒頁連結串列,當需要額外的頁時就會被重用。

空閒頁連結串列是由freelist trunkpage相互連線組成的連結串列,而每個freelist trunk page是由freelist leaf page(可以為零)組成的。

一個freelist trunk page是由一個4位元組大端整數陣列組成。陣列是在可用空間中放入儘可能多的整數。它的最小可用空間是480位元組,所以陣列最少可以放120整數。陣列中的第一個整數指向連結串列中的下一個freelist trunk page,如果為零就代表這是連結串列中的最後一個freelist trunk page。第二個整數代表所含有的freelist leaf page指標的數目。將第二個整數稱為L,如果L大於零,那麼陣列中2到L+1之間的每個整數就表示一個freelist leaf page。

在freelist leaf page中則什麼都沒有。

2.5.1.5 B-tree頁(B-treePages)

SQLite中使用了兩種B-tree。其中一種是b+tree,將所用的資料儲存在葉子節點,在SQLite中被稱為table b-tree。另一種是最初未經修改的b-tree,枝幹節點和葉子節點都含有關鍵字和資料,在SQLite中被稱為index b-tree。

Btree頁內部以單元為單位來組織資料,一個單元包含一個(或部分,當使用溢位頁時)payload(也稱為Btree記錄)。由於各類資料大小各不相同,每個單元的大小也就是可變的,所以Btree頁內部的空間需要進行動態分配(程式內部動態分配,不是動態申請空間),單元是Btree頁內部進行空間分配和回收的基本單位。每一個Btree頁包括三個部分:頁頭,單元指標陣列以及單元內容區。Page1除包括頁頭外,還包括100位元組的檔案頭,其結構如圖2.6所示。頁內所有單元的內容集中在頁的底部,稱為“單元內容區”,由下向上增長。而單元指標陣列則含有單元內容區每個單元的偏移量(2 byte),順序排列,偏於對單元內容進行插入和查詢,由上而下增長。

檔案頭(只有page 1有)

頁頭

單元指標陣列

未分配空間

單元內容區

圖2.6 Btree頁結構

其頁頭格式如表2.3所示。其中Flags定義了Btree頁的型別。標識leaf表示這個也是否有孩子。標識zerodata表示這個頁只有記錄沒有資料。標識intkey表示整數關鍵字存放在key size entry,而不是payload區域。

表2.3 Btree頁頭格式

偏移量

大小

說明

0

1

Flags. 1: intkey, 2: zerodata, 4: leafdata, 8: leaf

1

2

byte offset to the first freeblock

3

2

number of cells on this page

5

2

first byte of the cell content area

7

1

number of fragmented free bytes

8

4

Right child (the Ptr(N) value).  Omitted on leaves.

Flags可以表示四種類型的Btree頁,其值如下:

Ø  如果是B+tree的葉子頁,該位元組值為0X0D,

Ø  如果是B+tree的內部頁,該位元組值為0X05,

Ø  如果是B-tree的葉子頁,該位元組值為0X0A,

Ø  如果是B-tree的內部頁,該位元組值為0X02。

而這四種Btree頁的單元格式如表2.4所示。

表2.4 Btree頁單元格式

資料型別

Appears in...

說明

Table Leaf (0x0d)

Table Interior (0x05)

Index Leaf (0x0a)

Index Interior (0x02)

4-byte integer

Page number of left child

varint

Number of bytes of payload

varint

Rowid

byte array

Payload

4-byte integer

Page number of first overflow page

2.5.1.6 Cell Payload OverflowPages

當btree頁的一個btree單元的payload太大時,超出的部分機會放入溢位頁。這些溢位頁會形成一個連結串列。每個溢位頁的前四個位元組都是大端整數,指的是在連結串列上下一頁的頁碼,不過如果為零的話就是指它是最後一頁。之後的有效位元組都是用來儲存溢位內容。其結構如圖2.7所示。

圖2.7 溢位頁

2.5.1.7 Pointer Map or PtrmapPages

Pointer map或者ptrmap頁都是額外插入資料庫用來使auto_vacuum和incremental_vacuum操作更加高效的附加頁。

資料庫中其他的頁都是從從雙親節點指向孩子節點,而ptrmap頁正好相反。