cmu15445 2023spring Project #2 - B+Tree

B+Tree索引

插入与删除

主要需要注意分裂与合并。分裂时,叶子节点是先分裂再插入数据,而内部节点需要先插入再分裂。
删除时,先删除,如果size小于minsize则需要向左右兄弟节点借数据,如果还不行则合并。

有一个特殊情况就是没有兄弟节点,尽管这在真实索引使用中是不可能的,但是在在线测试中,有些测试中internal_max_size = 3,这就会发生没有兄弟节点的情况。对于这种情况,笔者的处理是只删除数据不删除节点,但是在使用迭代器时,需要对operator++进行特殊处理。

迭代器

笔者的数据成员如下

  BufferPoolManager *bpm_{nullptr};
  page_id_t leaf_page_id_{INVALID_PAGE_ID};
  int index_{0};

而在operator++中需要跳过一些空节点,核心代码如下

while (leaf_page_id_ != INVALID_PAGE_ID && index_ >= leaf_page_ptr_->GetSize()) {
  index_ = 0;
  leaf_page_id_ = leaf_page_ptr_->GetNextPageId();
  read_guard_page = bpm_->FetchPageRead(leaf_page_id_);
  leaf_page_ptr_ = read_guard_page.As<BPlusTreeLeafPage<KeyType, ValueType, KeyComparator>>();
}

并发

笔者的并发策略基于这样的一个判断:发生分裂/合并的插入/删除操作占比较小。因此,对于插入操作,第一次插入,只获取叶子节点的写锁,如果不需要分裂,则插入后返回,否则进行第二次插入。
第二次插入,获取根节点到页子节点的写锁,从叶子往上,进行递归插入。
删除操作同理。
这样做的好处是,对于第一次插入,不会阻塞读操作,同时也能支持对不同叶子节点的插入操作。

debug

对于本地测试,可以使用vscode debug。比较难的是并发的debug,这里说下笔者印象较深的bug,就是是FetchWritePage中,bpm锁、pin_count和page写锁的管理,最后通过的代码是:先获取bpm锁,然后pin_count++,然后释放bpm锁,再申请page写锁。
除了互斥资源,锁的释放如果被阻塞了,也可能引发死锁。

对于在线测试,笔者只会用经典方式:cout,打印一些信息来推测大概哪里出问题。

测试与总结

到提交为止(2023-05-13),gradescope上有42人完成了project2。由于缓冲区管理用的是全局锁,笔者的B+树并发性能算是一般。
在这里插入图片描述

笔者之前也写过B+Tree,但是是单线程内存B+Tree,这次多线程磁盘B+Tree还是挺困难的。
相较于内存索引,磁盘索引还需要管理缓存区。
另外两者的I/O粒度也不同,前者是cpu cacheline,后者是磁盘页/块。这导致两者在设计的着重点也不同,内存B+Tree的节点较小,需要充分利用每一个字节。

总之,这次实验增加了对面向磁盘B+Tree的理解,积累了并发编程以及debug的经验。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值