本文记录了 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 提交代码