CHECKPOINT #2
- 一、题目链接
- 二、准备工作
- 三、部分实现
- 1.锁操作
- 操作类型定义
- 安全页面判断
- 加锁操作
- 解锁操作
- 叶子页面查找操作
- 2.查找操作
- 3.插入操作
- 4.删除操作
- 四、评测结果
一、题目链接
二、准备工作
见 CMU 15-445 Project #0 - C++ Primer 中的准备工作。
三、部分实现
1.锁操作
操作类型定义
enum class OpType { FIND, INSERT, DELETE };
安全页面判断
/**
* 判断当前页面(节点)是否为安全页面(安全页面是指操作后不会影响树的结构的页面):
* 如果操作为查找,则所有页面均为安全页面;
* 如果操作为插入,则没有满的页面为安全页面;
* 如果操作为删除,则超过半满的页面为安全页面。
* @param page 待处理的B+树页面
* @param type 操作类型
*/
INDEX_TEMPLATE_ARGUMENTS
auto BPLUSTREE_TYPE::IsSafePage(BPlusTreePage *page, OpType type) -> bool {
if (type == OpType::FIND) {
return true;
}
if (type == OpType::INSERT) {
return page->GetSize() < (page->IsLeafPage() ? page->GetMaxSize() - 1 : page->GetMaxSize());
}
if (type == OpType::DELETE) {
/* 不安全的根页面会影响root_page_id_ */
if (page->IsRootPage()) {
return page->GetSize() > (page->IsLeafPage() ? 1 : 2);
}
return page->GetSize() > page->GetMinSize();
}
assert(false);
return false;
}
加锁操作
/**
* 对root_page_id_加共享锁或排他锁
* @param type 操作类型
*/
INDEX_TEMPLATE_ARGUMENTS
void BPLUSTREE_TYPE::LockRoot(Transaction *transaction, OpType type) {
if (type == OpType::FIND) {
root_mutex_.lock_shared();
} else {
root_mutex_.lock();
}
/* 空指针专门用于标识对root_mutex_的访问 */
transaction->AddIntoPageSet(nullptr);
}
/**
* 对缓冲池页面加共享锁或排他锁
* @param page 待加锁的缓冲池页面
* @param type 操作类型
*/
INDEX_TEMPLATE_ARGUMENTS
void BPLUSTREE_TYPE::LockPage(Page *page, Transaction *transaction, OpType type) {
if (type == OpType::FIND) {
page->RLatch();
} else {
page->WLatch();
}
/* 如果当前页面为安全页面,则对其所有祖先页面执行解锁和解固定。 */
if (IsSafePage(reinterpret_cast<BPlusTreePage *>(page->GetData()), type)) {
UnlockAllPages(transaction, type);
}
transaction->AddIntoPageSet(page);
}
解锁操作
/**
* 对事务中的所有缓冲池页面执行解锁和解固定操作
* @param transaction 存储了所有祖先页面的事务
* @param type 操作类型
*/
INDEX_TEMPLATE_ARGUMENTS
void BPLUSTREE_TYPE::UnlockAllPages(Transaction *transaction, OpType type) {
auto page_set = transaction->GetPageSet();
while (!page_set->empty()) {
auto cur_page = page_set->front();
if (type == OpType::FIND) {
if (cur_page == nullptr) {
root_mutex_.unlock_shared();
} else {
cur_page->RUnlatch();
buffer_pool_manager_->UnpinPage(cur_page->GetPageId(), false);
}
} else {
if (cur_page == nullptr) {
root_mutex_.unlock();
} else {
cur_page->WUnlatch();
buffer_pool_manager_->UnpinPage(cur_page->GetPageId(), true);
}
}
page_set->pop_front();
}
}
/**
* 对事务中指定缓冲池页面及其所有被加锁的子页面执行解锁和解固定操作,这个页面实际上就是一个待删除的页面,
* 为了避免它在解锁后删除前被其他线程操作,因此需要单独释放该页面及其子页面的写锁,而不能释放它的父页面的锁。
* @param page_id 最上层的待解锁的页面的page_id
* @param transaction 存储了所有祖先页面的事务
*/
INDEX_TEMPLATE_ARGUMENTS
void BPLUSTREE_TYPE::UnlockPartialPage(page_id_t page_id, Transaction *transaction) {
auto page_set = transaction->GetPageSet();
auto cur_page = page_set->back();
while (cur_page->GetPageId() != page_id) {
cur_page->WUnlatch();
buffer_pool_manager_->UnpinPage(cur_page->GetPageId(), true);
page_set->pop_back();
cur_page = page_set->back();
}
}
叶子页面查找操作
INDEX_TEMPLATE_ARGUMENTS
auto BPLUSTREE_TYPE::FindLeafPage(const KeyType &key, Transaction *transaction, OpType type) -> LeafPage * {
auto page = buffer_pool_manager_->FetchPage(root_page_id_);
LockPage(page, transaction, type);
auto cur_page = reinterpret_cast<BPlusTreePage *>(page->GetData());
while (!cur_page->IsLeafPage()) {
auto internal_page = static_cast<InternalPage *>(cur_page);
/* 查找下一层待处理的页面 */
int index = 1;
while (index < cur_page->GetSize() && comparator_(key, internal_page->KeyAt(index)) >= 0) {
index++;
}
page = buffer_pool_manager_->FetchPage(internal_page->ValueAt(index - 1));
LockPage(page, transaction, type);
cur_page = reinterpret_cast<BPlusTreePage *>(page->GetData());
}
return static_cast<LeafPage *>(cur_page);
}
2.查找操作
INDEX_TEMPLATE_ARGUMENTS
auto BPLUSTREE_TYPE::GetValue(const KeyType &key, std::vector<ValueType> *result, Transaction *transaction) -> bool {
bool new_transaction = false; // 标识是否新建了新的事务类
if (transaction == nullptr) {
transaction = new Transaction(0);
new_transaction = true;
}
LockRoot(transaction, OpType::FIND);
/* B+树为空 */
if (root_page_id_ == INVALID_PAGE_ID) {
UnlockAllPages(transaction, OpType::FIND);
if (new_transaction) {
delete transaction;
}
return false;
}
LeafPage *target_leaf_page = FindLeafPage(key, transaction, OpType::FIND);
for (int i = 0; i < target_leaf_page->GetSize(); i++) {
if (comparator_(key, target_leaf_page->KeyAt(i)) == 0) {
/* 查找成功 */
result->emplace_back(target_leaf_page->ValueAt(i));
UnlockAllPages(transaction, OpType::FIND);
if (new_transaction) {
delete transaction;
}
return true;
}
}
/* 查找失败 */
UnlockAllPages(transaction, OpType::FIND);
if (new_transaction) {
delete transaction;
}
return false;
}
3.插入操作
INDEX_TEMPLATE_ARGUMENTS
auto BPLUSTREE_TYPE::Insert(const KeyType &key, const ValueType &value, Transaction *transaction) -> bool {
bool new_transaction = false; // 标识是否新建了新的事务类
if (transaction == nullptr) {
transaction = new Transaction(0);
new_transaction = true;
}
LockRoot(transaction, OpType::INSERT);
/* B+树为空 */
if (root_page_id_ == INVALID_PAGE_ID) {
auto new_root_page = reinterpret_cast<LeafPage *>(buffer_pool_manager_->NewPage(&root_page_id_)->GetData());
/* 初始化新的根页面 */
new_root_page->Init(root_page_id_, INVALID_PAGE_ID, leaf_max_size_);
new_root_page->InsertByKey(key, value, comparator_);
new_root_page->SetNextPageId(INVALID_PAGE_ID);
UpdateRootPageId(true);
buffer_pool_manager_->UnpinPage(new_root_page->GetPageId(), true);
UnlockAllPages(transaction, OpType::INSERT);
if (new_transaction) {
delete transaction;
}
return true;
}
LeafPage *target_leaf_page = FindLeafPage(key, transaction, OpType::INSERT);
/* key重复 */
if (!target_leaf_page->InsertByKey(key, value, comparator_)) {
UnlockAllPages(transaction, OpType::INSERT);
if (new_transaction) {
delete transaction;
}
return false;
}
/* 叶子页面上溢 */
if (target_leaf_page->GetSize() == target_leaf_page->GetMaxSize()) {
HandleLeafOverflow(target_leaf_page);
}
UnlockAllPages(transaction, OpType::INSERT);
if (new_transaction) {
delete transaction;
}
return true;
}
INDEX_TEMPLATE_ARGUMENTS
void BPLUSTREE_TYPE::HandleLeafOverflow(LeafPage *target_page) {
if (target_page->IsRootPage()) {
page_id_t split_page_id;
auto split_page = reinterpret_cast<LeafPage *>(buffer_pool_manager_->NewPage(&split_page_id)->GetData());
auto new_root_page = reinterpret_cast<InternalPage *>(buffer_pool_manager_->NewPage(&root_page_id_)->GetData());
/* 初始化分裂页面 */
split_page->Init(split_page_id, root_page_id_, leaf_max_size_);
target_page->MoveHalfDataTo(split_page);
/* 初始化新的根页面 */
new_root_page->Init(root_page_id_, INVALID_PAGE_ID, internal_max_size_);
new_root_page->SetKeyAt(0, split_page->KeyAt(0)); // 无任何实际意义的填充值
new_root_page->SetValueAt(0, target_page->GetPageId());
new_root_page->SetKeyAt(1, split_page->KeyAt(0));
new_root_page->SetValueAt(1, split_page->GetPageId());
new_root_page->IncreaseSize(1);
target_page->SetParentPageId(root_page_id_); // 将新根页面设置为旧根页面的父页面
UpdateRootPageId(false);
buffer_pool_manager_->UnpinPage(split_page->GetPageId(), true);
buffer_pool_manager_->UnpinPage(new_root_page->GetPageId(), true);
return;
}
page_id_t split_page_id;
auto split_page = reinterpret_cast<LeafPage *>(buffer_pool_manager_->NewPage(&split_page_id)->GetData());
auto parent_page = reinterpret_cast<InternalPage *>(buffer_pool_manager_->FetchPage(target_page->GetParentPageId())->GetData());
/* 初始化分裂页面 */
split_page->Init(split_page_id, parent_page->GetPageId(), leaf_max_size_);
target_page->MoveHalfDataTo(split_page);
/* 判断父页面是否上溢 */
if (parent_page->GetSize() == parent_page->GetMaxSize()) {
HandleInternalOverflow(parent_page, split_page->KeyAt(0), split_page->GetPageId());
} else {
parent_page->InsertByKey(split_page->KeyAt(0), split_page->GetPageId(), comparator_, buffer_pool_manager_);
}
/* parent_page再次Fetch时并未放入事务中,因此要单独Unpin一次。 */
buffer_pool_manager_->UnpinPage(parent_page->GetPageId(), true);
buffer_pool_manager_->UnpinPage(split_page->GetPageId(), true);
}
INDEX_TEMPLATE_ARGUMENTS
void BPLUSTREE_TYPE::HandleInternalOverflow(InternalPage *target_page, const KeyType &key, const page_id_t &value) {
if (target_page->IsRootPage()) {
page_id_t split_page_id;
auto split_page = reinterpret_cast<InternalPage *>(buffer_pool_manager_->NewPage(&split_page_id)->GetData());
auto new_root_page = reinterpret_cast<InternalPage *>(buffer_pool_manager_->NewPage(&root_page_id_)->GetData());
/* 初始化分裂页面 */
split_page->Init(split_page_id, root_page_id_, internal_max_size_);
target_page->MoveHalfDataAndInsertTo(split_page, key, value, comparator_, buffer_pool_manager_); // split_page首个key暂时有效
/* 初始化新的根页面 */
new_root_page->Init(root_page_id_, INVALID_PAGE_ID, internal_max_size_);
new_root_page->SetKeyAt(0, split_page->KeyAt(0)); // 无任何实际意义的填充值
new_root_page->SetValueAt(0, target_page->GetPageId());
new_root_page->SetKeyAt(1, split_page->KeyAt(0));
new_root_page->SetValueAt(1, split_page->GetPageId());
new_root_page->IncreaseSize(1);
target_page->SetParentPageId(root_page_id_); // 将新根页面设置为旧根页面的父页面
UpdateRootPageId(false);
buffer_pool_manager_->UnpinPage(split_page->GetPageId(), true);
buffer_pool_manager_->UnpinPage(new_root_page->GetPageId(), true);
return;
}
page_id_t split_page_id;
auto split_page = reinterpret_cast<InternalPage *>(buffer_pool_manager_->NewPage(&split_page_id)->GetData());
auto parent_page = reinterpret_cast<InternalPage *>(buffer_pool_manager_->FetchPage(target_page->GetParentPageId())->GetData());
/* 初始化分裂页面 */
split_page->Init(split_page_id, target_page->GetParentPageId(), internal_max_size_);
target_page->MoveHalfDataAndInsertTo(split_page, key, value, comparator_, buffer_pool_manager_); // split_page首个key暂时有效
/* 判断父页面是否上溢 */
if (parent_page->GetSize() == parent_page->GetMaxSize()) {
HandleInternalOverflow(parent_page, split_page->KeyAt(0), split_page->GetPageId());
} else {
parent_page->InsertByKey(split_page->KeyAt(0), split_page->GetPageId(), comparator_, buffer_pool_manager_);
}
/* parent_page再次Fetch时并未放入事务中,因此要单独Unpin一次。 */
buffer_pool_manager_->UnpinPage(parent_page->GetPageId(), true);
buffer_pool_manager_->UnpinPage(split_page->GetPageId(), true);
}
4.删除操作
INDEX_TEMPLATE_ARGUMENTS
void BPLUSTREE_TYPE::Remove(const KeyType &key, Transaction *transaction) {
bool new_transaction = false; // 标识是否新建了新的事务类
if (transaction == nullptr) {
transaction = new Transaction(0);
new_transaction = true;
}
LockRoot(transaction, OpType::DELETE);
/* B+树为空 */
if (root_page_id_ == INVALID_PAGE_ID) {
UnlockAllPages(transaction, OpType::DELETE);
if (new_transaction) {
delete transaction;
}
return;
}
LeafPage *target_leaf_page = FindLeafPage(key, transaction, OpType::DELETE);
/* key不存在 */
if (!target_leaf_page->RemoveByKey(key, comparator_)) {
UnlockAllPages(transaction, OpType::DELETE);
if (new_transaction) {
delete transaction;
}
return;
}
/* 叶子页面下溢 */
if ((target_leaf_page->GetSize() < target_leaf_page->GetMinSize())) {
if (!target_leaf_page->IsRootPage()) {
/* 非根叶子页面下溢 */
HandleLeafUnderflow(target_leaf_page, transaction);
} else if (target_leaf_page->GetSize() == 0) {
/* 根叶子页面下溢且根节点为空 */
root_page_id_ = INVALID_PAGE_ID;
UpdateRootPageId(false);
UnlockPartialPage(target_leaf_page->GetPageId(), transaction);
buffer_pool_manager_->DeletePage(target_leaf_page->GetPageId());
}
}
UnlockAllPages(transaction, OpType::DELETE);
if (new_transaction) {
delete transaction;
}
}
INDEX_TEMPLATE_ARGUMENTS
void BPLUSTREE_TYPE::HandleLeafUnderflow(LeafPage *target_page, Transaction *transaction) {
int tar_index;
int bro_index;
Page *bpage = nullptr;
auto parent_page = reinterpret_cast<InternalPage *>(buffer_pool_manager_->FetchPage(target_page->GetParentPageId())->GetData());
auto bro_page = static_cast<LeafPage *>(GetBrotherPage(parent_page, target_page, tar_index, bro_index, bpage));
/* 如果target_page发生了下溢,说明它一定不是安全页面,
* 那么当前线程在Fetch父页面parent_page前就已经在查找target_page时将其固定过一次了,
* 因此为了简化代码,可以在获取后直接Unpin。 */
buffer_pool_manager_->UnpinPage(parent_page->GetPageId(), true);
/* 从兄弟页面借取 */
if (bro_page->GetSize() > bro_page->GetMinSize()) {
if (bro_index < tar_index) {
/* 从左兄弟借最后一个数据 */
KeyType bro_last_key = bro_page->KeyAt(bro_page->GetSize() - 1);
ValueType bro_last_value = bro_page->ValueAt(bro_page->GetSize() - 1);
bro_page->RemoveByKey(bro_last_key, comparator_);
target_page->InsertByKey(bro_last_key, bro_last_value, comparator_);
parent_page->SetKeyAt(tar_index, bro_last_key);
} else {
/* 从右兄弟借第一个数据 */
KeyType bro_first_key = bro_page->KeyAt(0);
ValueType bro_first_value = bro_page->ValueAt(0);
bro_page->RemoveByKey(bro_first_key, comparator_);
target_page->InsertByKey(bro_first_key, bro_first_value, comparator_);
parent_page->SetKeyAt(bro_index, bro_page->KeyAt(0));
}
/* parent_page再次Fetch时并未放入事务中,因此要单独Unpin一次。 */
bpage->WUnlatch();
buffer_pool_manager_->UnpinPage(bro_page->GetPageId(), true);
return;
}
/* 将页面向左合并 */
LeafPage *left_page; // 保存左侧没有被删除的子页面
if (bro_index < tar_index) {
/* left_bro <- target */
left_page = bro_page;
target_page->MoveAllDataTo(bro_page);
parent_page->RemoveByIndex(tar_index);
/* 兄弟页面不在transaction中,而且此时还持有父页面的锁,因此移动后单独归还即可。 */
bpage->WUnlatch();
buffer_pool_manager_->UnpinPage(bro_page->GetPageId(), true);
/* 将页面解锁后删除,在此过程需要维护其父页面的锁。 */
UnlockPartialPage(target_page->GetPageId(), transaction);
buffer_pool_manager_->DeletePage(target_page->GetPageId());
} else {
/* target <- right_bro */
left_page = target_page;
bro_page->MoveAllDataTo(target_page);
parent_page->RemoveByIndex(bro_index);
/* 将页面解锁后删除 */
bpage->WUnlatch();
buffer_pool_manager_->UnpinPage(bro_page->GetPageId(), true);
buffer_pool_manager_->DeletePage(bro_page->GetPageId());
}
if (parent_page->GetSize() < parent_page->GetMinSize()) {
if (!parent_page->IsRootPage()) {
/* 非根内部页面下溢 */
HandleInternalUnderflow(parent_page, transaction);
} else if (parent_page->GetSize() == 1) {
/* 根内部页面下溢且此根页面仅有left_page一个孩子 */
root_page_id_ = left_page->GetPageId();
left_page->SetParentPageId(INVALID_PAGE_ID);
UpdateRootPageId(false);
UnlockPartialPage(parent_page->GetPageId(), transaction);
buffer_pool_manager_->DeletePage(parent_page->GetPageId());
}
}
}
INDEX_TEMPLATE_ARGUMENTS
void BPLUSTREE_TYPE::HandleInternalUnderflow(InternalPage *target_page, Transaction *transaction) {
/* 从缓冲池获取兄弟页面及相关下标 */
int tar_index;
int bro_index;
Page *bpage = nullptr;
auto parent_page = reinterpret_cast<InternalPage *>(buffer_pool_manager_->FetchPage(target_page->GetParentPageId())->GetData());
auto bro_page = static_cast<InternalPage *>(GetBrotherPage(parent_page, target_page, tar_index, bro_index, bpage));
buffer_pool_manager_->UnpinPage(parent_page->GetPageId(), true);
/* 从兄弟页面借取 */
if (bro_page->GetSize() > bro_page->GetMinSize()) {
if (bro_index < tar_index) {
/* 从左兄弟借最后一个数据 */
KeyType bro_last_key = bro_page->KeyAt(bro_page->GetSize() - 1);
page_id_t bro_last_value = bro_page->ValueAt(bro_page->GetSize() - 1);
bro_page->RemoveByValue(bro_last_value);
target_page->SetKeyAt(0, parent_page->KeyAt(tar_index)); // 临时填充首个key
target_page->InsertByIndex(0, bro_last_key, bro_last_value, comparator_, buffer_pool_manager_);
parent_page->SetKeyAt(tar_index, bro_last_key);
} else {
/* 从右兄弟借第一个数据 */
KeyType bro_first_key = parent_page->KeyAt(bro_index);
page_id_t bro_first_value = bro_page->ValueAt(0);
bro_page->RemoveByValue(bro_first_value);
target_page->InsertByIndex(target_page->GetSize(), bro_first_key, bro_first_value, comparator_, buffer_pool_manager_);
parent_page->SetKeyAt(bro_index, bro_page->KeyAt(0));
}
/* parent_page再次Fetch时并未放入事务中,因此要单独Unpin一次。 */
bpage->WUnlatch();
buffer_pool_manager_->UnpinPage(bro_page->GetPageId(), true);
return;
}
/* 将页面向左合并 */
InternalPage *left_page; // 保存左侧没有被删除的子页面
if (bro_index < tar_index) {
/* left_bro <- target */
left_page = bro_page;
target_page->SetKeyAt(0, FindFistKey(target_page)); // 临时填充首个key
target_page->MoveAllDataTo(bro_page, comparator_, buffer_pool_manager_);
parent_page->RemoveByIndex(tar_index);
/* 兄弟页面不在transaction中,因此需要单独归还 */
bpage->WUnlatch();
buffer_pool_manager_->UnpinPage(bro_page->GetPageId(), true);
/* 将页面解锁后删除,在此过程需要维护其父页面的锁。 */
UnlockPartialPage(target_page->GetPageId(), transaction);
buffer_pool_manager_->DeletePage(target_page->GetPageId());
} else {
/* target <- right_bro */
left_page = target_page;
bro_page->SetKeyAt(0, FindFistKey(bro_page)); // 临时填充首个key
bro_page->MoveAllDataTo(target_page, comparator_, buffer_pool_manager_);
parent_page->RemoveByIndex(bro_index);
/* 将页面解锁后删除 */
bpage->WUnlatch();
buffer_pool_manager_->UnpinPage(bro_page->GetPageId(), true);
buffer_pool_manager_->DeletePage(bro_page->GetPageId());
}
if (parent_page->GetSize() < parent_page->GetMinSize()) {
if (!parent_page->IsRootPage()) {
/* 非根内部页面下溢 */
HandleInternalUnderflow(parent_page, transaction);
} else if (parent_page->GetSize() == 1) {
/* parent_page为根且仅有left_page一个孩子 */
root_page_id_ = left_page->GetPageId();
left_page->SetParentPageId(INVALID_PAGE_ID);
UpdateRootPageId(false);
UnlockPartialPage(parent_page->GetPageId(), transaction);
buffer_pool_manager_->DeletePage(parent_page->GetPageId());
}
}
}
INDEX_TEMPLATE_ARGUMENTS
auto BPLUSTREE_TYPE::GetBrotherPage(InternalPage *parent_page, BPlusTreePage *child_page, int &target_index, int &bro_index, Page *&bpage) -> BPlusTreePage * {
target_index = parent_page->GetIndexByValue(child_page->GetPageId());
/* 只有左兄弟 */
if (target_index == parent_page->GetSize() - 1) {
bpage = buffer_pool_manager_->FetchPage(parent_page->ValueAt(target_index - 1));
bpage->WLatch();
auto bro_page = reinterpret_cast<BPlusTreePage *>(bpage->GetData());
bro_index = target_index - 1;
return bro_page;
}
/* 只有右兄弟 */
if (target_index == 0) {
bpage = buffer_pool_manager_->FetchPage(parent_page->ValueAt(target_index + 1));
bpage->WLatch();
auto bro_page = reinterpret_cast<BPlusTreePage *>(bpage->GetData());
bro_index = target_index + 1;
return bro_page;
}
/* 既有左兄弟也有右兄弟 */
auto lpage = buffer_pool_manager_->FetchPage(parent_page->ValueAt(target_index - 1));
lpage->WLatch();
auto lbro_page = reinterpret_cast<BPlusTreePage *>(lpage->GetData());
auto rpage = buffer_pool_manager_->FetchPage(parent_page->ValueAt(target_index + 1));
rpage->WLatch();
auto rbro_page = reinterpret_cast<BPlusTreePage *>(rpage->GetData());
/* 左兄弟优先 */
if (rbro_page->GetSize() > rbro_page->GetMinSize() && lbro_page->GetSize() < lbro_page->GetMinSize()) {
lpage->WUnlatch();
buffer_pool_manager_->UnpinPage(lbro_page->GetPageId(), false); // Unpin不需要的兄弟页面
bro_index = target_index + 1;
bpage = rpage;
return rbro_page;
}
rpage->WUnlatch();
buffer_pool_manager_->UnpinPage(rbro_page->GetPageId(), false); // Unpin不需要的兄弟页面
bro_index = target_index - 1;
bpage = lpage;
return lbro_page;
}
四、评测结果
参考:
https://blog.csdn.net/Altair_alpha/article/details/129270169