CMU 15-445 Project #2 - B+Tree(CHECKPOINT #2)

news2024/11/25 8:21:33

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

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/774957.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

linux安装conda

linux安装conda 卸载conda 在主目录下&#xff0c;使用普通权限安装&#xff1a; ./Anaconda3-2023.03-1-Linux-x86_64.shanaconda的目录是ENTER

139. 单词拆分

139. 单词拆分 原题链接&#xff1a;完成情况&#xff1a;解题思路&#xff1a;参考代码&#xff1a; 原题链接&#xff1a; 139. 单词拆分 https://leetcode.cn/problems/word-break/ 完成情况&#xff1a; 解题思路&#xff1a; dp动态递归去接&#xff0c;算0-n所有范围…

Xshell 7 评估期已过继续免费使用方法

1. 评估期已过的示例 2.解决方法 如果需要继续使用&#xff0c;一是去网上寻找绿色版本的Xshell&#xff0c;但是可能不安全。 二是重新下载一个免费版本&#xff0c;覆盖安装即可。 2.1 官网下载地址&#xff1a;https://www.xshell.com/zh/free-for-home-school/ 2.2下载安…

Pytorch:利用torchvision调用各种网络的预训练模型,完成CIFAR10数据集的各种分类任务

2023.7.19 cifar10百科&#xff1a; [ 数据集 ] CIFAR-10 数据集介绍_cifar10_Horizon Max的博客-CSDN博客 torchvision各种预训练模型的调用方法&#xff1a; pytorch最全预训练模型下载与调用_pytorch预训练模型下载_Jorbol的博客-CSDN博客 CIFAR10数据集下载并转换为图片&am…

gitlab配置公钥

1、打开本地git bash,使用如下命令生成ssh公钥和私钥对 ssh-keygen -t rsa -C yourEmailgitlab.com2、然后打开~/.ssh/id_rsa.pub文件&#xff0c;复制里面的内容 cd ~/.ssh ls cat ./id_rsa.pub3、打开gitlab,找到Profile Settings–>SSH Keys—>Add SSH Key,并把上一…

【多线程】(六)Java并发编程深度解析:常见锁策略、CAS、synchronized原理、线程安全集合类和死锁详解

文章目录 一、常见锁策略1.1 乐观锁和悲观锁1.2 读写锁1.3 重量级锁和轻量级锁1.4 自旋锁1.5 公平锁和非公平锁1.6 可重入锁和不可重入锁 二、CAS2.1 什么是CAS2.2 CAS的实现原理2.3 CAS应用2.4 ABA问题 三、synchronized原理3.1 synchronized锁的特点3.2 加锁工作过程3.3 锁消…

VM(CentOS7安装和Linux连接工具以及换源)

目录 一、Linux意义 二、安装VMWare 三、centos7安装 1、正式安装CentOS7&#xff1a; 2、安装不了的解决方案 2.1常见问题——虚拟机开机就黑屏的完美解决办法 3、查看、设置IP地址 ① 查看ip地址&#xff1a;ip addr 或者 ifconfig&#xff0c; 注意与windows环境的区别…

labelme+sam在windows上使用指南

其实官网讲的很清楚了&#xff0c;这里做一个笔记&#xff0c;方便自己后面直接看。 首先&#xff0c;贴一下官方的链接&#xff0c;作者老哥很强&#xff0c;respect&#xff01; 使用流程&#xff1a; https://github.com/wkentaro/labelme#installation 资源&#xff1a; ht…

数据库锁机制

锁机制 1. 概述2. 并发事务的不同场景2.1 读-读情况2.2 写-写情况2.3 读-写或写-读情况2.3.1 方案一&#xff1a;读事务使用MVCC&#xff08;多版本并发控制&#xff09;&#xff0c;写事务加锁2.3.2 方案二&#xff1a;读、写事务均加锁 3. 锁分类3.1 从数据操作类型&#xff…

1.Docker概念

文章目录 Docker概念Docker容器与虚拟机的区别内核中的2个重要技术Linux Namespace的6大类型docker三个重要概念部署Dockeryum安装二进制安装 Docker 概念 docker是一个开源的应用容器引擎&#xff0c;基于go语言开发并遵循了apache2.0协议开源。docker可以让开发者打包他们的…

【PostgreSQL内核学习(三)—— 查询重写】

查询重写 查询重写系统规则视图和规则系统ASLO型规则的查询重写规则系统与触发器的区别 查询重写的处理操作定义重写规则删除重写规则对查询树进行重写 声明&#xff1a;本文的部分内容参考了他人的文章。在编写过程中&#xff0c;我们尊重他人的知识产权和学术成果&#xff0c…

王道计算机网络学习笔记(4)——网络层

前言 文章中的内容来自B站王道考研计算机网络课程&#xff0c;想要完整学习的可以到B站官方看完整版。 四&#xff1a;网络层 ​​​​​​​​​​​​​​在计算机网络中&#xff0c;每一层传输的数据都有不同的名称。 物理层&#xff1a;传输的数据称为比特&#xff08;Bi…

宝塔的Redis绑定IP

宝塔安装Redis 软件商店搜索Redis 连接宝塔面板的redis服务器失败的解决办法 检查Linux是否放行6379端口修改Redis绑定IP检查阿里云/腾讯云的防火墙策略是否放行6379端口 1.bind 127.0.0.1 修改为 bind 0.0.0.0 127.0.0.1 表示只允许本地访问,无法远程连接 0.0.0.0 表…

基于Python的用户登录和密码强度等级测试|Python小应用

前言 那么这里博主先安利一些干货满满的专栏了&#xff01; 这两个都是博主在学习Linux操作系统过程中的记录&#xff0c;希望对大家的学习有帮助&#xff01; 操作系统Operating Syshttps://blog.csdn.net/yu_cblog/category_12165502.html?spm1001.2014.3001.5482Linux S…

Micro-app vue3+vite+ts用法

前言&#xff1a; 微前端的概念是由ThoughtWorks在2016年提出的&#xff0c;它借鉴了微服务的架构理念&#xff0c;核心在于将一个庞大的前端应用拆分成多个独立灵活的小型应用&#xff0c;每个应用都可以独立开发、独立运行、独立部署&#xff0c;再将这些小型应用融合为一个…

文库小程序在线阅读下载文档模板流量主小程序

一、什么是文库小程序&#xff1f; 文库小程序连接流量主&#xff0c;具体流程是粉丝进入小程序下载文档模板&#xff0c;下载前需要看广告&#xff0c;阅读后可以免费下载文档模板。具体的小程序演示请参见抖音云云文库 二、文库小程序的应用范围 小程序主要实现文档共享功能…

Redis Linux安装

Redis版本下载,版本地址http://download.redis.io/releases/ 点击跳转 新建文件夹 mkdir /usr/local/redis 上传压缩包,并使用命令解压tar -zxvf redis-6.2.8.tar.gz (redis-6.2.8.tar.gz为安装包) 安装依赖 yum install gcc-c 编译 make 安装 make install 修改配置 …

概率论和随机过程的学习和整理20:条件概率我知道,但什么是条件期望?可用来解决递归问题

目录 1 目标问题&#xff1a; 什么是条件期望&#xff1f; 条件期望有什么用&#xff1f; 2 条件期望&#xff0c;全期望公式 3 条件期望&#xff0c;全期望公式 和 条件概率&#xff0c;全概率公式的区别和联系 3.1 公式如下 3.2 区别和联系 3.3 概率和随机过程 4 有什…

Zabbix“专家坐诊”第200期问答汇总

问题一 Q&#xff1a;想请问下大佬们&#xff0c;我们zabbix最近有误告警的情况&#xff0c;这个怎么排查呢&#xff1f; 用了proxy&#xff0c;我看了proxy和server的日志&#xff0c;除了有慢查的日志&#xff0c;其它没有异常日志输出。 A&#xff1a;看下这个unreachable的…

首次与电商平台战略签约 第一三共与阿里健康达成战略合作

7月18日&#xff0c;阿里健康与第一三共在杭州正式签署战略合作协议。双方宣布将在此前合作基础上&#xff0c;全面深化心脑血管、风湿骨外科等疾病领域的合作深度&#xff0c;探索以患者为中心、以数字化为驱动力的创新型医药健康服务模式。据悉&#xff0c;此次合作是第一三共…