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

在这里插入图片描述

一、题目链接


二、准备工作

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

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
提供的源码资源涵盖了安卓应用、小程序、Python应用和Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。
提供的源码资源涵盖了安卓应用、小程序、Python应用和Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。
提供的源码资源涵盖了安卓应用、小程序、Python应用和Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值