依然是,按照模板完成的一块,而且可能完成度不太高。
在这个过程中虽然自己有调试树,但是最后验收的时候数据量比较小,而buffer和record的结合完成的比较晚,所以测试仅仅只限于小部分的输入。写树的部分的时候感觉应该用C++写的……测试过程中如果能用C++来完成初始化会好很多……
发现二分查找很难(要不是一开始写了我就遍历了……)相当于写了三种不同的二分查找,找具体位置、找空格、找前包含的位置(查找路过中间节点),然后就……一直在调整这几个二分查找。
还有就是自己(好像总感觉从cBlock里调一个值蛮快的没必要写函数balabala),在一个block里取具体值的那个公式(value.Mid(8+ (3 + type_size)* n, type_size)之类)复制黏贴了无数次……感觉自己缺少相应的规划,还有一个函数体结构一大自己的变量名就认不出来了……感觉都长得差不多……value、block、value_temp1,诸如此类……以后还是要养成好的习惯!~
最后感慨一下这个存储方式的神奇orz我之前只能用指针去表示子节点……
然后,因为最后验收的时候没有压测所以在测试的时候只对比较简单的情况进行了处理……可能这棵树还有很多问题……
一.模块分析:
Index manager是程序的索引部分,直接对buffer manager提供的内存索引块操作,主要是构建B+树,负责实现record manager这一模块(提供函数接口)所需要的函数。功能有:存储\删除记录的索引,查找记录在table表中的相对位置等(由buffer负责写回磁盘)。
二.设计思路:
块的设计:把4kb的block作为B+树的节点,在每个block的开始用一个不同字符代替叶子块和中间块,同时紧跟block中的value的数目以及value值,同时对索引文件中的空块链成单链表,由索引文件信息结构体的freenum保存第一个空块的块号,其余每块头三个字节保存下一个空块的块号,最后一个保存字符’000’。详细图如下:
中间节点:
开始为?标记为叶子节点。
_ttoi(node_value.Mid(1, 4))为有几个节点
value.Mid(8 + (3 + type_size)* n, type_size)为第n个索引值
value.Mid(5 + (3 + type_size)* n, 3))为第n个叶子节点的编号
叶子节点:
开始为!标记为叶子节点。
_ttoi(node_value.Mid(1, 4))为有几个数据
value.Mid(10 + (5 + type_size)* n, type_size)为第n个数值
value.Mid(10 + (5 + type_size)* n, type_size)为第n个数值的offset.
最后三位为下一个叶子节点。如果是#表示该节点已为最后一个叶子节点。
空节点的存储方式同buffer。
三、函数介绍:
实现的所有接口函数如下:
1.查找一个值等于inform .value(inform为info结构体)的记录在table表中的记录号:
int search_one(CString database, CStringtable_name, struct index_info & inform);
参数说明:
database:数据库名,cstring型;
table_name:表格文件名,cstring型
inform:info结构体,是引用。
函数结束返回时,记录号保存在inform .offset中,若没有,inform.offset=0。
返回值:
-7 inform .offset=0 索引文件为空
-8 inform .offset=0 表中无记录
-1 inform .offset=0 所查值的类型错误(int,float,char(n))
-3 inform .offset=0 读到异常块
正数 叶子块号,若inform .offset>0,记录存在表文件中,记录号为inform .offset
正数 叶子块号,若inform .offset=0,记录不在表文件中,返回的叶子块号在函数insert_one中要用(因为将值插入改块就够了)
思路:按照b+树的原理,遍历中间和叶子节点,直到找到其值,返回。
2.查找一批>, >=, <, <=inform . value(inform为info结构体)的记录在table表中的记录号:
void search_many(CString database,CStringtable_name,int& start,int& end,int type,
structindex_info& inform) ;
参数说明:
database:数据库名,cstring型;
table_name:表格文件名,cstring型
inform:info结构体,是引用,
type: 1 是 找出所有 > inform .value; 的记录;
2 是 找出所有 >= inform .value 的记录;
3 是 找出所有 < inform .value 的记录;
4 是 找出所有 <= inform .value 的记录;
函数返回时:
若没有: start=0;
否则: start=索引文件中所需记录所在的叶子节点的起始块号
end=索引文件中所需记录所在的叶子节点的终止块号
inform . offset =索引文件start起始块中的第一个所需值的偏移量(>,>=)
或则
=索引文件end 终止块中的最后一个所需值的偏移量(<,<=)
思路:分两类考虑,按照b+树的原理,遍历中间和叶子节点,直到找到起始值或终止值所在的块时,返回。
3.插入一个值的函数,值和类型在参数inform结构体中:
3.1.voidinsert_one(CString database, CString table_name, struct index_info & inform);
先调用search_one函数,找到要插入的叶子块块号,并判断是否值已经存在,若不存在,当叶子未满时,则找到位子插入,否则,调用insert_divide函数分割,再插入。
3.2.到叶子块满时调用函数:
void insert_divide(CString database,CStringtable_name,struct index_info& inform,
int leaf1,int leaf2,char* leafpoint2);
在这里为了简化后续的插入操作,在插入一个数的时候,判断插入完成后是否可以再插入一个值。如果插入该节点后该块被填满,调用该函数分割。
重复调用这个函数直到将leafpoint2插入之后不满足分裂的条件。
在找到leaf1的父节点为根节点的时候需要特殊处理,将新申请的块和根节点的编号交换(要保证根节点的块编号是001,因为是通过这个方法寻找根节点的。)
参数说明:
leaf1:原来满的叶子块的块号,此块已经分割好
leaf2:新的块号,此块保存有原来满的叶子块leaf1的右半部分
leafpoint2:传递字符串,在分裂后两个节点,后一个子块的向上传递的字符。
4.CStringint_to_str(int value);
int 型数值转化成5字节的string类型。
5.intfind_prev_leaf_sibling(CString database,CString table_name, struct index_infoinform,
int nodenum);
找出所在叶子块的前一个兄弟叶子块,调用find_left_child函数,找出最左孩子,并判断是否与nodenum相等,不相等的话,遍历所有孩子,找到nodenum块时,返回他的前面节点,否则,返回0。
返回值:
-1 函数中读到错误块;
0 没有前一个兄弟叶子块(即此块为最左孩子)
>0 前一个兄弟叶子块的块号
6.intfind_next_leaf_sibling(CString database,CString table_name,struct index_infoinform,
int nodenum);
找出所在叶子块的后一个兄弟叶子块,读取nodenum对应叶子块块号,根据值(最后三位)返回叶子块。
返回值:
-1 函数中读到错误块;
0 没有后一个兄弟叶子块(即此块为最右孩子)
>0 后一个兄弟叶子块的块号
7.intfind_left_child (CString database,index_info inform);
找出最左叶子块的块号
返回值:
-1 函数中读到错误块;
0 索引文件空
>0 最左叶子块的块号
8.intfind_right_child(CString database,index_info inform);
找出最右叶子块的块号。
返回值:
-1 函数中读到错误块;
0 索引文件空
>0 最右叶子块的块号
9.intget_new_freeblocknum(CString database,CString table_name,struct index_info&inform);
调用buffer的函数findBlock,返回块号。
10.intfind_father(CString database,CString table_name,index_info inform, int num);
根据B+树的原理,查找值为inform .value所在的叶子节点,若中途碰到块号为num的数据块,则返回他的父亲节点。
返回值 -1 函数中读到错误块;
0 没有父亲块(即此块为根)
>0 父亲块的块号
11.void delete_one(CString database,CStringtable_name,struct index_info & inform);
调用search_one找到inform对应的位置,把对应的offset值置为0.
12. int compare(CString x, CString y, structindex_info& inform);
根据inform.type对两个参数转换之后,对两个参数进行比较。
返回值:x>y返回1,x==y返回0,x<y返回-1。
13. int find_pos(CString value, CString des_value, inttype_size, index_info inform);
二分查找,主要应用于查找。
对于叶节点,返回该叶节点的位置,不存在返回-1。
对于中间节点,返回该节点所在区间的位置。
14. int find_insert(CString value, CString des_value,int type_size, index_