引子
书接上回,前面分析了三种B树节点的结构,现在还剩下最后一种。说实话,我是宁愿写代码也懒得写文档的人。因为写代码有编译器帮你检查错误 ,写文档就只有自已检查错误。而我又经常犯一些超低级错误,写在文档里让人看见,真是很没有面子。可是现在能坐在电脑前的时间越来越少,再不抓紧时间写点东西。以后就是想写,也是有心无力了。树欲静而风不止,人欲睡而电不停,奈何!奈何!
正式开始
上一篇文章说到只要在数据库中建立了索引,就会产生索引B树节点,上一次先通过数据库的第一页找到索引对象的根页号,然后检查这个根页号,发现这一页果然就是索引B树内部节点。
咱们将这一部分结果再看一下:
PageNo -> 5
0x0000 -> Flags : 2[0x0002][IndexBTreeInterior]
0x0001 -> offseFirstFreeblock : 0
0x0003 -> numberOfCells : 2
0x0005 -> firstByteOfCellContent : 966[0x03c6]
0x0007 -> numberOfFragmentedFreeBytes : 0
0x0008 -> RightChildPageNo : 10
cellPointArrayOffset->12[0x000c]
cellCount->2
leafPageNo->8[0x0008]
col1 type:text size:20 -> 43240219900103600066
col2 type:i8 size:1 -> 66
leafPageNo->9[0x0009]
col1 type:text size:20 -> 43240219900103800040
col2 type:i8 size:1 -> 40
从上面最后三行可以看出9号页是5号页的孩子,我们怀疑这个9号就是索引B树叶子节点。其实它不是叶子节点也没有关系,咱们就一直这样追踪下去,一定会找到叶子节点。它毕竟是一个深度有限的B树,又不是一个无底洞。
下面就是对9号页的分析结果
PageNo -> 9
0x0000 -> Flags : 10[0x000a][IndexBTreeLeaf]
0x0001 -> offseFirstFreeblock : 0
0x0003 -> numberOfCells : 36
0x0005 -> firstByteOfCellContent : 125[0x007d]
0x0007 -> numberOfFragmentedFreeBytes : 0
0x0008 -> RightChildPageNo : 0
cellPointArrayOffset->8[0x0008]
cellCount->36
col1 type:text size:20 -> 43240219900103600068
col2 type:i8 size:1 -> 68
col1 type:text size:20 -> 43240219900103600073
col2 type:i8 size:1 -> 73
col1 type:text size:20 -> 43240219900103600076
col2 type:i8 size:1 -> 76
col1 type:text size:20 -> 43240219900103600079
col2 type:i8 size:1 -> 79
col1 type:text size:20 -> 43240219900103600082
col2 type:i8 size:1 -> 82
col1 type:text size:20 -> 43240219900103600087
col2 type:i8 size:1 -> 87
col1 type:text size:20 -> 43240219900103600088
col2 type:i8 size:1 -> 88
col1 type:text size:19 -> 4324021990010360009
col2 type:i8 size:1 -> 9
col1 type:text size:20 -> 43240219900103600090
col2 type:i8 size:1 -> 90
col1 type:text size:20 -> 43240219900103600092
col2 type:i8 size:1 -> 92
col1 type:text size:20 -> 43240219900103600094
col2 type:i8 size:1 -> 94
col1 type:text size:20 -> 43240219900103700016
col2 type:i8 size:1 -> 16
col1 type:text size:20 -> 43240219900103700020
col2 type:i8 size:1 -> 20
col1 type:text size:20 -> 43240219900103700022
col2 type:i8 size:1 -> 22
col1 type:text size:20 -> 43240219900103700025
col2 type:i8 size:1 -> 25
col1 type:text size:20 -> 43240219900103700026
col2 type:i8 size:1 -> 26
col1 type:text size:20 -> 43240219900103700032
col2 type:i8 size:1 -> 32
col1 type:text size:20 -> 43240219900103700051
col2 type:i8 size:1 -> 51
col1 type:text size:20 -> 43240219900103700059
col2 type:i8 size:1 -> 59
col1 type:text size:20 -> 43240219900103700064
col2 type:i8 size:1 -> 64
col1 type:text size:20 -> 43240219900103700069
col2 type:i8 size:1 -> 69
col1 type:text size:20 -> 43240219900103700075
col2 type:i8 size:1 -> 75
col1 type:text size:20 -> 43240219900103700078
col2 type:i8 size:1 -> 78
col1 type:text size:20 -> 43240219900103700081
col2 type:i8 size:1 -> 81
col1 type:text size:20 -> 43240219900103700086
col2 type:i8 size:1 -> 86
col1 type:text size:20 -> 43240219900103700089
col2 type:i8 size:1 -> 89
col1 type:text size:20 -> 43240219900103700096
col2 type:i8 size:1 -> 96
col1 type:text size:20 -> 43240219900103700097
col2 type:i8 size:1 -> 97
col1 type:text size:20 -> 43240219900103800010
col2 type:i8 size:1 -> 10
col1 type:text size:20 -> 43240219900103800012
col2 type:i8 size:1 -> 12
col1 type:text size:20 -> 43240219900103800017
col2 type:i8 size:1 -> 17
col1 type:text size:20 -> 43240219900103800023
col2 type:i8 size:1 -> 23
col1 type:text size:20 -> 43240219900103800033
col2 type:i8 size:1 -> 33
col1 type:text size:20 -> 43240219900103800034
col2 type:i8 size:1 -> 34
col1 type:text size:20 -> 43240219900103800038
col2 type:i8 size:1 -> 38
col1 type:text size:20 -> 43240219900103800039
col2 type:i8 size:1 -> 39
哈哈,内容还真不少,从结果的第二行可以看出,这真是一个索引B树叶子节点。看来这个B树的深度只有2,也就是说只要搜索两个以上的节点就能找到想要的内容。索引真是一个好东西。
从结果上看, 索引B树叶子节点与 索引B树内部节点很像,只是少了一个孩子指针,因为它不需要,已经到底了。game over了。
上索引B树叶子节点的分析代码吧
//解包表索引B树(B树)叶子节点的单元内容
function unpackIndexBTreeLeafCell($workBench){
//参数区开始------------------------------------
$fin = $workBench['fin'];
$pageNo = $workBench['pageNo'];
$cellNo = $workBench['cellNo'];
$cellOffset = $workBench['cellOffset'];
$outputPath = $workBench['outputPath'];
//参数区结束------------------------------------
fseek($fin, $cellOffset,SEEK_SET);
//找到cell入口后,第一部分是变长整数,代表payload的大小
$PayloadOffset = 0;
$PayloadSize = readValueFromFile($fin, 'var','varInt',$info);
//echo 'PayloadSize->',intWithHexStr($PayloadSize),"\n";
$PayloadOffset +=$info['size'];
//echo 'PayloadOffset(in cell)->',intWithHexStr($PayloadOffset),"\n";
//设置输出结果的文件名
$resultFileName = $outputPath .'/page'.$pageNo.'-cell'.$cellNo.'-payload.json';
//--------------------------------------------------------------------------
//设置工作区参数
$newWorkBench['fin'] = $fin;
$newWorkBench['PayloadOffset'] = $cellOffset + $PayloadOffset;
$newWorkBench['resultFileName'] = $resultFileName;
//解包B树节点的payload内容(payload结构与页类型无关)
unpackBTreePayLoad($newWorkBench);
}
到目前为止,sqlite B树的4种类型的页节点都已经分析完成,就此收工。