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.

68

4

The "Application ID" set by PRAGMA application_id.

72

20

Reserved for expansion. Must be zero.

92

4

The version-valid-for number.

96

4

SQLITE_VERSION_NUMBER

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页正好相反。

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值