【数据库】cmu15445-project2 B+Tree Checkpoint 1 实验总结

发布时间 2023-06-14 11:59:03作者: stackupdown

 

project-2相比project-1难度提升了不少。

project2的工作量较大,因此分成两个checkpoint。checkpoint2是支持并发安全,而checkpoint1其实是数据结构的问题,这篇文章先写project-1的checkpoint1。

实验前提

可以看project-2的实验文档。由于课件里没有B+ Tree的具体插入删除规则,因此需要找数据库系统概念找到对应的伪代码,最好看英文版的内容。整个project的checkpoint1就是在这个数据结构上折腾。

D:\Gitlab\DB-Lab\cs15445-docs\image-20230606110216758.png

实验内容

总体实现的接口

checkpoint1需要实现tree如下接口:Remove Insert GetValue

迭代器接口:Begin End operator++

整个project的任务(22fall的任务标题有误导,主要不是实现数据结构,而是实现增加和删除)

Task #2a - B+Tree Insertions

Task #2b - B+Tree Deletions

Task #2c – Index Iterator

Task #2d – Concurrency Control

对应的leaf节点和internal节点类的实现按照需要添加各自的接口

1.核心伪代码

1.1 插入

2.删除:

2.数据结构的数量关系

代码确实比较长,所以在实现之前最好先搞清楚数据存储的结构。具体来说,叶子节点跟内部节点存在最大,最小值的区别,而内部节点的v都是page_id,叶子节点的v是多样的。

内部节点因为孩子数=kv对数+1,所以用数组保存的时候,index=0的位置的key是无效的。

叶子节点的结构:

/*

+----------+------+----------+------+-----+-------------+----------+----------+

| Pointer1 | Key1 | Pointer2 | Key2 | ... | Pointer(N-1)| Key(N-1) | PointerN |

+----------+------+----------+------+-----+-------------+----------+----------+

*/

内部节点的结构:

第i个节点指向的子树中的任意K,满足K(i) <= K < K(i+1),这里n=size-1,KEY(0)无效

/*

+----------+------+----------+------+-----+-------------+----------+----------+

| KEY(0)+PAGE_ID(0) | KEY(1)+PAGE_ID(1) | KEY(2)+PAGE_ID(2) | ... | KEY(n)+PAGE_ID(n) |

+----------+------+----------+------+-----+-------------+----------+----------+

*/

数量关系遵循如下约定:

internal的size=key数+1=指针数;

leaf的size=key数=value数;

节点类型与size大小

叶子节点

内部节点

最小值

作为根节点=1;非根节点=floor(max_size/2)

作为根节点=2,非根节点=floor((max_size+ 1) / 2)

最大值

leaf_max_size

internal_max_size

key数

size

size – 1

插入的分裂条件

插入后size>=leaf_max_size

插入后size>internal_max_size

删除的合并条件

删除后与另一节点的size之和∈[min_size, max_size)

插入后与兄弟节点的size之和∈[min_size, max_size]

删除的重新分布条件(拆借)

删除后与另一节点的size之和>=max_size

插入后与兄弟节点的size之和>max_size

由于internal的size跟leaf的size表示的内容其实都是以数组大小为准,所以判断分裂条件和其他的时候都有差为1的调整。

其中,叶子节点最小的max_size可以是2,而内部节点最小的max_size是3。否则它不满足B+树的形态,空叶子节点和只有1个孩子的内部节点是不合法的,此时需要做删除、合并的调整。

3. 拆借

插入的分裂和删除的拆借是不同的。分裂之后节点的元素均匀分布,而拆借只需从兄弟节点取得1个节点即可。

3.二分法的细节

节点的查找、删除都用到了节点内部的key有序的性质,所以有好几个函数会出现二分查找的要求。但是查找要注意查找失败的时候要返回什么,这里建议一开始先确定并注释Lookup,Index等函数的含义,如返回未找到的下一个index(类似C++里的lower_bound)。

而对于根据page_id查找叶子节点的要求(寻找兄弟节点),则一般靠遍历。这里没有测试过性能的差别,但是page_id依次比较的性能应该与二分法比较多个key的性能差不多。

实验技巧

1.debug技巧

如果不是抄代码的,在第一次实现的过程中大概率会遇到很多问题。建议写一个dump的函数。列出如下:

在每次insert和remove的时候output出对应的树的图片,就可以直观的发现问题。

同时使用官方提供的printer可以对照发现自己实现的问题。

2.测试用例技巧

对于插入,删除,除了简单的批量插入,批量删除,还可以用以下的方式,构造随机插入删除的数组。

References

https://15445.courses.cs.cmu.edu/fall2022 课程官网

https://github.com/cmu-db/bustub Bustub Github Repo

Database System Concepts 6th version, Abraham.Silberschatz.

自动测评网站 GradeScope,course entry code: PXWVR5 https://www.gradescope.com/