sqlite源码分析之路(二) 数据库文件格式探索(5) 数据表B树内部节点分析

26 篇文章 2 订阅
9 篇文章 1 订阅

引子

书接上回,前面看了数据表B树叶子节点。接下来要看一看数据表B树内部节点。数据表B树内部节点是4种B树节点中结构最简单的,为什么?
它没有payload。它里面的单元不包含具体的记录数据,只包含关键值与左孩子指针,在逻辑上形成树形结构。

正式开始

要想得到一个数据表B树内部节点,最简单的办法就是向sqlite数据表中多插入一些数据,当数据在一页内放不下时,内部节点就出现了。B树的深度也就增长了。学过数据结构的人都知道。B树是一个平衡树。通过把线性查找变成树查找,查找效率提高了指数倍。所以任何一个像样一点的数据库产品,都会使用B树来提高查找效率。

这次选一个内容多一点的数据库,它的大小有41k。按照上一篇讲的老办法,先将数据库文件切分成多个独立的页文件。
还是先拿第一页做实验,下图是对第一页进行分析得到的结果

PageNo -> 1
0x0000 -> Flags : 13[0x000d][TableBTreeLeaf]
0x0001 -> offseFirstFreeblock : 789
0x0003 -> numberOfCells : 4
0x0005 -> firstByteOfCellContent : 472[0x01d8]
0x0007 -> numberOfFragmentedFreeBytes : 0
0x0008 -> RightChildPageNo : 0

cellPointArrayOffset->108[0x006c]
cellCount->4

col1 type:text     size:5      -> table
col2 type:text     size:15     -> sqlite_sequence
col3 type:text     size:15     -> sqlite_sequence
col4 type:i8       size:1      -> 3
col5 type:text     size:38     -> CREATE TABLE sqlite_sequence(name,seq)

col1 type:text     size:5      -> table
col2 type:text     size:8      -> customer
col3 type:text     size:8      -> customer
col4 type:i8       size:1      -> 4
col5 type:text     size:203    -> CREATE TABLE customer
	        (id INTEGER PRIMARY KEY AUTOINCREMENT ,
	        customerId TEXT   NOT NULL,
	        customerName TEXT   NOT NULL,
	        idCard TEXT ,
	        remark TEXT  
	        )

col1 type:text     size:5      -> table
col2 type:text     size:12     -> tmp_customer
col3 type:text     size:12     -> tmp_customer
col4 type:i8       size:1      -> 2
col5 type:text     size:88     -> CREATE TABLE tmp_customer(customerId text,customerName text,logTime text,netWeight text)

col1 type:text     size:5      -> index
col2 type:text     size:6      -> idcard
col3 type:text     size:8      -> customer
col4 type:i8       size:1      -> 5
col5 type:text     size:51     -> CREATE INDEX "idcard"
ON "customer" ("idCard" ASC)

从上面的结果来看,第1页的页标志是0x0d,它还是一个数据表B树叶子节点。因为大部分的数据都插到了一个叫做customer的表中,并没有在sql_master表中增加什么内容。

我们接着查看上面结果中的单元部分,会发现如下内容

col1 type:text     size:5      -> table
col2 type:text     size:8      -> customer
col3 type:text     size:8      -> customer
col4 type:i8       size:1      -> 4
col5 type:text     size:203    -> CREATE TABLE customer
	        (id INTEGER PRIMARY KEY AUTOINCREMENT ,
	        customerId TEXT   NOT NULL,
	        customerName TEXT   NOT NULL,
	        idCard TEXT ,
	        remark TEXT  
	        )

注意到 “col4 type:i8 size:1 -> 4” 这说明customer表的根页号是4。而我们已经往customer表中插入了不少数据。因此页面4很可能是一个内部节点。

那让我们来检查一下这个4号页,看看它的庐山真面目吧。

一个数据表B树内部节点


PageNo -> 4
0x0000 -> Flags : 5[0x0005][TableBTreeInterior]
0x0001 -> offseFirstFreeblock : 0
0x0003 -> numberOfCells : 4
0x0005 -> firstByteOfCellContent : 1004[0x03ec]
0x0007 -> numberOfFragmentedFreeBytes : 0
0x0008 -> RightChildPageNo : 38

cellPointArrayOffset->12[0x000c]
cellCount->4

leafPageNo->6[0x0006]
cellKey->24
leafPageNo->25[0x0019]
cellKey->48
leafPageNo->35[0x0023]
cellKey->72
leafPageNo->36[0x0024]
cellKey->96

从显示的结果的第二行可以看出,这正是一个数据表B树内部节点。它包含4个单元,它的最右孩子是第38页

它的4个单元的内容是:

eafPageNo->6[0x0006]
cellKey->24
leafPageNo->25[0x0019]
cellKey->48
leafPageNo->35[0x0023]
cellKey->72
leafPageNo->36[0x0024]
cellKey->96

这4个单元分别指向6号页,15号页,35号与36号页,再加上它的最右孩子38号页,说明这个内部节点有5个孩子。

下面看一下实现分析功能的代码

数据表B树内部节点分析函数

//解包表B树(B+树)内部节点的单元内容,内部节点的结构比较简单,不用再调用其它函数进行分解
function unpackTableBTreeInteriorCell($workBench){

//参数区开始------------------------------------
	$fin = $workBench['fin'];
	$pageNo = $workBench['pageNo'];
	$cellNo = $workBench['cellNo'];
	$cellOffset = $workBench['cellOffset'];
	$outputPath = $workBench['outputPath'];
//参数区结束------------------------------------

	fseek($fin, $cellOffset,SEEK_SET);
	//找到cell入口后,第一部分是4字节整数,指向叶子节点的页号,第二部分是变长整数,代表关键字key(i)
	
	$leafPageNo = readValueFromFile($fin, 4,'u32',$info);
	echo 'leafPageNo->',intWithHexStr($leafPageNo),"\n";
	$cellKey = readValueFromFile($fin, 'var','varInt',$info);
	echo 'cellKey->',$cellKey,"\n";

	$resultArray = ['leafPageNo'=>$leafPageNo,'cellKey'=>$cellKey];

	//设置输出结果的文件名
	$resultFileName = $outputPath .'/page'.$pageNo.'-cell'.$cellNo.'-payload.json';

	//echo "\nunpack over!";
	//echo "\n\n\n";	

	//将结果以json形式写入文件
	file_put_contents($resultFileName, json_encode($resultArray));	

}

这是4种节点分析中最简单的。从代码上也可以看得出来,不需要再调用对payload的解析。因为它的单元中根本就不包含payload信息

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值