SQLite文件格式分析_v102 下载本文

源码网资料下载:www.codepub.com 0X00000007:为Ptr(0),指向第7页。

0X0A:Payload数据的字节数。数据从03~0F。 0X03:记录头包括3个字节。

0X19:字段1。TEXT,长度为:(25-13)/2=6。字段值为:cookie。

0X01:字段2。整数,长度为1。字段值为:0X0F,表示索引值cookie所对应记录的关键值(即记录的rowid值)为15。

注:如果是通过索引字段查找“cookie”,现在就可以按其rowid值=15到数据B+tree树中去检索记录的全部数据了。

0X03DC单元的内容如下:(图中深蓝色部分)

0X00000008:为Ptr(1),指向第8页。

0X10:Payload数据的字节数。数据从03~21。 0X03:记录头包括3个字节。

0X25:字段1。TEXT,长度为:(37-13)/2=12。字段值为:Peach Muffin。

0X01:字段2。整数,长度为1。字段值为:0X21,表示索引值Peach Muffin所对应记录的关键值为33。

3.3 索引页的叶子页格式

对于B-tree来说,内部页与叶子页的格式差别其实不大。简单分析一下,为了验证上一节的结果。

文件第8页前半部分的内容如下:

页头的第1个字节值为0X0A,说明该页为B-tree的叶子页。其它不再详述。

单元内容区的开始部分内容如下:

单元内容区的开始和结束部分内容如下:

不再详细分析了,粗看可以看出:

此页记录的索引值确实都在cookie和Peach Muffin之间,不包括这两个值(这两个值已经在根页中了)。单元数据都按索引值大小排序。

下载源码就到源码网,www.codepub.com

源码网资料下载:www.codepub.com 4 碎片、自由块和空闲页

到目前为止,我们只对数据库表进行了插入操作,因此,文件格式还是很“整齐”的。如果对数据库进行删除和修改操作,就会产生碎片、自由块和空闲页。本文1.2节和1.3节对相应的概念有较详细介绍,本章就算是对前述内容的例证吧。

4.1 碎片

执行下面的update语句,每执行一句都将原记录的name字段长度减少2字节(可与前文的insert语句对照)。

update foods set name='Bavarian Cream P' where id=3;

执行完上面语句后,文件第3页的页头内容如下图所示:

可以看到,偏移为7的字节值变成了2,说明当前页中有2字节的碎片。再执行下面语句: update foods set name='Bear Cla' where id=4;

可以看到当前页中的碎片字节数已经变成了4,如下图所示:

至于碎片字节数到多大才会整理,这得分析源程序了。

4.2 自由块

执行下面删除语句:

delete from foods where id=5; 文件第3页的页头内容如下:

可以看到,第1个自由块的偏移量为0X0396。 观察0X0396处的单元,数据如下图所示:

单元的前2个字节指向下一个空闲块,此处值为0,表示没有下一个空闲块了。 0X001E表示该空闲块的大小,从数值上来分析,应该是包含了这两个字节本身。 如果再删除1条记录,就可以看到自由块是如何串接的,我们就不做了。

4.3 空闲页

执行下面删除语句:

delete from foods where id>10; 文件大小仍为9K,但此时文件中应该有了6个空闲页(索引和数据都只需要一个页就能放下了),分别是页3、4、5、7、8和页9。 观察此时的文件头,内容为:

下载源码就到源码网,www.codepub.com

源码网资料下载:www.codepub.com

注意其中深蓝色部分,可以看到,偏移为32的4个字节中值为0X00000005,表示空闲页链表首指针指向第5个页。对了,就应该是第5个页先空闲出来。

偏移为36的4个字节中值为0X00000006,表示文件中空闲页的数量为6。

现在得介绍一下空闲页链表的格式了。 空闲页有两种类型:trunk page(主干页)和leaf page(叶子页)。文件头偏移为32处的指针指向空闲链表的第一个trunk page,每个trunk page指向多个叶子页。偏移36处的4个字节为空闲页的总数量,包括所有的trunk page和leaf page。空闲页链表的结构如下图所示:

文件头 空闲页链表首指针 trunk page 下一个trunk page leaf page数量 … trunk page 下一个trunk page leaf page数量 … … trunk page 下一个trunk page leaf page数量 … leaf page leaf page … leaf page 其中,trunk page的格式(从页的起始处开始)如下:

(1)4个字节,指向下一个trunk page的页号,0表示链表结束; (2)4个字节,该页leaf page的数量;

(3)0个或多个指向leaf page的页号,每项4个字节。

文件第6页前部的内容如下:

说明:

下一个trunk page的页号为0X00000000,表示链表中只有此一个trunk page,没有后继结点了。

该页leaf page的数量为0X00000005,表示本页带有5个leaf page,依次为0X00000009,0X00000008,0X00000007、0X00000004和0X00000003。

SQLite对leaf page的格式没有规定。

5 溢出页的格式

下载源码就到源码网,www.codepub.com

源码网资料下载:www.codepub.com 5.1 溢出页格式说明

如前所述,单元(cell)具有可变的大小,而页的大小是固定的,这就有可能一个单元比一个完整的页还大,这样的单元就会溢出到由溢出页组成的链表上,如下图所示:

Btree页 单元头 Payload 单元头 Payload(第1部分) 溢出页号 溢出页号 溢出页 Payload(第2部分) … 0 溢出页 Payload(最后部分) 在上图中,Btree页的最后一个单元超大,需要使用溢出页。此时,单元的最后4个字节为溢出页链表中第1个溢出页的页号。对于每个溢出页,其头4个字节为下一溢出页的页号,该值为0时表示此页为溢出页链表的表尾页。

除最后一个溢出页外,每个溢出页全部填充数据(除了最开始的4个字节)。最后一个溢出页可能数据很少,甚至只有一个字节的数据,但一个溢出页不会存储来自两个单元的数据。

5.2 准备数据库

得换一个数据库了。我们用下面的方法来分析溢出页的格式,并对上节内容进行验证。 执行SQLite的命令行工具,创建一个新的数据库food_text.db。 D:\\SQLite\\CLP>sqlite3 foods_text.db 创建一个新表。

CREATE TABLE foods( id integer primary key, type_id integer, name text );

插入1条记录,该记录包含一段较长的文本。 INSERT INTO \ALUES(1, 1,

'0000000001000000000200000000030000000004000000009 0000000001000000000200000000030000000004000000009 0000000001000000000200000000030000000004000000009 0000000001000000000200000000030000000004000000009 0000000001000000000200000000030000000004000000009 0000000001000000000200000000030000000004000000009 下载源码就到源码网,www.codepub.com