本文记录了 PROJECT #2 - B+TREE 的实验过程
索引 负责快速地检索数据,而不需要搜索数据库表中的每一行;索引还提供了快速的随机访问以及高效地访问有序记录。因此,在这个编程作业中,我们需要实现基于 B+ 树的动态索引结构 。
CHECKPOINT #1 TASK #1 - B+TREE PAGES 实现三个 Page 类来存储 B+ 树的数据
B+Tree Parent Page
B+Tree Internal Page
B+Tree Leaf Page
b_plus_tree_page.cpp 的实现
直接根据函数要求设置或返回对应的字段即可。
IsRootPage 函数根据 parent_id_ 是否是 INVALID_PAGE_ID 返回 true 或者 false
GetMinSize():需要区分 1)根结点且为叶子结点。2)根结点。3)其他
b_plus_tree_internal_page.cpp 的实现
MappingType 的类型为 std::pair<KeyType, ValueType>,如何获取第 i 个 MappingType?=> 在其头文件有一个 array 的 MappingType 数组(array[0] 表示这是一个弹性数组)
注意有些函数需要判断 index 是否合法,使用 assert()
将 bpm 取出的页面转换为 internal page/leaf page(重要)
1 2 3 4 5 6 Page *page = buffer_pool_manager_->FetchPage(page_id); assert(page != nullptr ); BPlusTreeInternalPage *bpt_node = reinterpret_cast <BPlusTreeInternalPage *>(page->GetData()); buffer_pool_manager_->UnpinPage(bpt_node->GetPageId(), flag);
1 2 3 4 5 6 7 8 KeyType KeyAt (int index) const ;void SetKeyAt (int index, const KeyType &key) ;int ValueIndex (const ValueType &value) const ;ValueType ValueAt (int index) const ;
1 2 3 4 ValueType Lookup (const KeyType &key, const KeyComparator &comparator) const ;
1 2 3 4 5 6 7 8 9 10 11 void PopulateNewRoot (const ValueType &old_value, const KeyType &new_key, const ValueType &new_value) ;int InsertNodeAfter (const ValueType &old_value, const KeyType &new_key, const ValueType &new_value) ;
1 2 3 4 5 6 7 8 9 void Remove (int index) ;ValueType RemoveAndReturnOnlyChild () ;
Split and Merge utility methods
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 void MoveHalfTo (BPlusTreeInternalPage *recipient, BufferPoolManager *buffer_pool_manager) ;void CopyNFrom (MappingType *items, int size , BufferPoolManager *buffer_pool_manager) ;void MoveAllTo (BPlusTreeInternalPage *recipient, const KeyType &middle_key, BufferPoolManager *buffer_pool_manager) ;void MoveFirstToEndOf (BPlusTreeInternalPage *recipient, const KeyType &middle_key, BufferPoolManager *buffer_pool_manager) ;void CopyLastFrom (const MappingType &pair, BufferPoolManager *buffer_pool_manager) ;void MoveLastToFrontOf (BPlusTreeInternalPage *recipient, const KeyType &middle_key, BufferPoolManager *buffer_pool_manager) ;void CopyFirstFrom (const MappingType &pair, BufferPoolManager *buffer_pool_manager) ;
注:Merge 和 Redistribute 的几个函数可以放在 CP2 中完成
b_plus_tree_Leaf_page.cpp 的实现
1 2 3 4 5 6 7 8 9 10 page_id_t GetNextPageId () const ;void SetNextPageId (page_id_t next_page_id) ;KeyType KeyAt (int index) const ;int KeyIndex (const KeyType &key, const KeyComparator &comparator) const ;const MappingType &GetItem (int index) ;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 int Insert (const KeyType &key, const ValueType &value, const KeyComparator &comparator) ;bool Lookup (const KeyType &key, ValueType *value, const KeyComparator &comparator) const ;int RemoveAndDeleteRecord (const KeyType &key, const KeyComparator &comparator) ;
Split and Merge utility methods
吐槽:上面的四个 Move 函数和 BPlusTreeInternalPage 类中的同名函数参数不一致,导致后面 Split 中调用报错。之后我将这一组函数的参数改为一致才编译通过。
TASK #2.A - B+TREE DATA STRUCTURE (INSERTION & POINT SEARCH)
FindLeafPage 这个函数是查找、插入、删除 操作的基础,它是找到 key 所在的叶子节点,leftMost = true 时表示返回 B+ 树中最左边的叶子节点
在 InsertIntoLeaf 函数中,如果插入后,叶子结点包含的 <K, V> 个数等于 GetMaxSize(),此时就要进行分裂
在 InsertIntoParent 函数中,插入后的中间结点包含的 <K, V> 个数大于 GetMaxSize() 才进行分裂
CHECKPOINT #2 TASK #3 - INDEX ITERATOR
迭代器的实现主要是注意什么时候结束?(到达最右边叶子节点的最后一个 slot)以及实现 ++ 操作时,处理下一个叶子节点的情况。
TASK #4 - CONCURRENT INDEX
coalesce 或者 redistribute 操作时需要获得 sibling 的 W Latch
为了保护 root_page_id_,采用了虚拟页 v_root_page,和一个 mutex_
Crabbing Locking(解锁顺序和加锁顺序要一致)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 INDEX_TEMPLATE_ARGUMENTS void BPLUSTREE_TYPE::LockThisPage (int op, Page *page, Transaction *transaction) {}INDEX_TEMPLATE_ARGUMENTS void BPLUSTREE_TYPE::UnlockPrevPage (int op, Transaction *transaction, bool is_dirty) {}
测试/验证/打包
1 2 3 4 5 6 7 cd buildmake b_plus_tree_insert_test ./test /b_plus_tree_insert_test make b_plus_tree_delete_test ./test /b_plus_tree_delete_test make b_plus_tree_concurrent_test ./test /b_plus_tree_concurrent_test
1 2 3 make format make check-lint make check-clang-tidy
1 2 3 4 5 6 7 zip project2-submission.zip src/include/buffer/lru_replacer.h src/buffer/lru_replacer.cpp \ src/include/buffer/buffer_pool_manager.h src/buffer/buffer_pool_manager.cpp \ src/include/storage/page/b_plus_tree_page.h src/storage/page/b_plus_tree_page.cpp \ src/include/storage/page/b_plus_tree_internal_page.h src/storage/page/b_plus_tree_internal_page.cpp \ src/include/storage/page/b_plus_tree_leaf_page.h src/storage/page/b_plus_tree_leaf_page.cpp \ src/include/storage/index/b_plus_tree.h src/storage/index/b_plus_tree.cpp \ src/include/storage/index/index_iterator.h src/storage/index/index_iterator.cpp
然后前往 https://www.gradescope.com 提交代码