DataBase 作业2(3) 教师:N Yang

本文仅用于作业提交

buffer_pool_manager

做到这里的时候,发现代码的封装性很强,很多的函数设置都在其它的文件中,在看懂源码和知晓源码所用函数的函数的基础上进行了修改。

buffer_pool_manager.h

/*
 * buffer_pool_manager.h
 *
 * Functionality: The simplified Buffer Manager interface allows a client to
 * new/delete pages on disk, to read a disk page into the buffer pool and pin
 * it, also to unpin a page in the buffer pool.
 */

#pragma once
#include <list>
#include <mutex>

#include "buffer/lru_replacer.h"
#include "disk/disk_manager.h"
#include "hash/extendible_hash.h"
#include "logging/log_manager.h"
#include "page/page.h"

namespace scudb {
class BufferPoolManager {
public:
  BufferPoolManager(size_t pool_size, DiskManager *disk_manager,
                          LogManager *log_manager = nullptr);

  ~BufferPoolManager();

  Page *FetchPage(page_id_t page_id);

  bool UnpinPage(page_id_t page_id, bool is_dirty);

  bool FlushPage(page_id_t page_id);

  Page *NewPage(page_id_t &page_id);

  bool DeletePage(page_id_t page_id);

private:
  size_t sizeofpool; // number of pages in buffer pool
  Page *pages_;      // array of pages
  DiskManager *disk_manager_;
  LogManager *log_manager_;
  HashTable<page_id_t, Page *> *page_table_; // to keep track of pages
  Replacer<Page *> *replacer_;   // to find an unpinned page for replacement
  std::list<Page *> *free_list_; // to find a free page for replacement
  std::mutex latch_;             // to protect shared data structure
  Page *GetVictimPage();
};
} // namespace cmudb

函数接口:

寻找页面:

 Page *FetchPage(page_id_t page_id);

修改pincount字段:

bool UnpinPage(page_id_t page_id, bool is_dirty);

更新页:

bool FlushPage(page_id_t page_id);

需要一个新页:

Page *NewPage(page_id_t &page_id);

删除页:

bool DeletePage(page_id_t page_id);

buffer_pool_manager.cpp

主要的函数如下:

获取页:

首先遍历,如果需要的页存在于页表,那么返回。否则找到需要替代的页,如果需要替代的页被修改,那么先执行写回。之后去除掉需要替代的页,并写入新页。最后修改新页的相关参数,便于后续使用。

//获取页
    Page* BufferPoolManager::FetchPage(page_id_t p_id) {
        //lock_guard<mutex> lck(latch_);
        Page* tg = nullptr;
        //首先遍历,如果有记录则返回
        if (page_table_->Find(p_id, tg)) { //1.1
            tg->pin_count_++;
            replacer_->Erase(tg);
            return tg;
        }
        //1.2
        //如果没有找到,那么就找到需要替代的页
        tg = GetVictimPage();
        if (tg == nullptr) return tg;
        //2
        //如果需要替换的页已被修改过,那么需要写回
        if (tg->is_dirty_) {
            disk_manager_->WritePage(tg->GetPageId(), tg->data_);
        }
        //3
        //去除需要去掉的页,在页表中插入需要获取的页
        page_table_->Remove(tg->GetPageId());
        page_table_->Insert(p_id, tg);
        //4
        //读取页并设置页的参数,以供后续的算法需要
        disk_manager_->ReadPage(p_id, tg->data_);
        tg->is_dirty_ = false;
        tg->page_id_ = p_id;
        tg->pin_count_ = 1;

        return tg;
    }

修改pincount值:

如果未找到或者目标页的pincount<=0,那么返回0,否则把pincount减一,如果减一后为0,把它放入replacer中。

 bool BufferPoolManager::UnpinPage(page_id_t p_id, bool ifdir) {
        //lock_guard<mutex> lck(latch_);
        Page* tg = nullptr;
        page_table_->Find(p_id, tg);
        //如果未找到,返回false
        if (tg == nullptr) {
            return false;
        }
        tg->is_dirty_ = ifdir;
        //如果pincount<=0,返回false
        if (tg->GetPinCount() <= 0) {
            return false;
        }
        //如果减一后,pincout为0,那么把它放入replacer里面
        tg->pin_count_--;
        if (tg->pin_count_ == 0) {
            replacer_->Insert(tg);
        }
        return true;
    }

更新页的isdirty值:

如果页未找到,那么返回false。如果找到,则把is_dirty_改为false,防止后面被错误替换。

//更新页的is_dirty_值,如果页未找到,那么返回false。如果找到,则把is_dirty_改为false,防止后面被错误替换
    bool BufferPoolManager::FlushPage(page_id_t p_id) {
        //lock_guard<mutex> lck(latch_);
        Page* tg = nullptr;
        page_table_->Find(p_id, tg);
        if (tg == nullptr || tg->page_id_ == INVALID_PAGE_ID) {
            return false;
        }
        if (tg->is_dirty_) {
            disk_manager_->WritePage(p_id, tg->GetData());
            tg->is_dirty_ = false;
        }

        return true;
    }

删除页:

//删除页
    bool BufferPoolManager::DeletePage(page_id_t p_id) {
        //lock_guard<mutex> lck(latch_);
        Page* tg = nullptr;
        page_table_->Find(p_id, tg);
        if (tg != nullptr) {
            if (tg->GetPinCount() > 0) {//如果目标页的pincount>0,则不应该被删除
                return false;
            }
            //否则删除页
            page_table_->Remove(p_id);
            replacer_->Erase(tg);
            tg->is_dirty_ = false;
            tg->ResetMemory();
            free_list_->push_back(tg);
        }
        disk_manager_->DeallocatePage(p_id);
        return true;
    }

获取新页:

如果free_list中有,那么就使用free_list的空间。否则,采用页的替换算法。

 Page* BufferPoolManager::GetVictimPage() {
        Page* tg = nullptr;
        if (!(free_list_->empty())) {
            tg = free_list_->front();
            free_list_->pop_front();
            if (tg->GetPageId() != INVALID_PAGE_ID)
            {
                printf("the target id is INVALID_PAGE_ID");
                exit(1);
            }
        }
        else {
            if (replacer_->Size() == 0) {
                return nullptr;
            }
            replacer_->Victim(tg);
        }
        if (tg->GetPinCount() != 0)
        {
            printf("the PinCount is zero!\n");
            exit(1);
        }
        return tg;
    }
Page* BufferPoolManager::NewPage(page_id_t& p_id) {
        //lock_guard<mutex> lck(latch_);
        Page* tg = nullptr;
        tg = GetVictimPage();
        if (tg == nullptr) return tg;

        p_id = disk_manager_->AllocatePage();
        //2
        //如果目标页被修改过
        if (tg->is_dirty_) {
            disk_manager_->WritePage(tg->GetPageId(), tg->data_);
        }
        //3
        //删除目标页,插入新页
        page_table_->Remove(tg->GetPageId());
        page_table_->Insert(p_id, tg);

        //4
        //修改相关参数
        tg->page_id_ = p_id;
        tg->ResetMemory();
        tg->pin_count_ = 1;
        tg->is_dirty_ = false;

        return tg;
    }

完整代码如下:

#include "buffer/buffer_pool_manager.h"

namespace scudb {

    /*
     * BufferPoolManager Constructor
     * When log_manager is nullptr, logging is disabled (for test purpose)
     * WARNING: Do Not Edit This Function
     */
    BufferPoolManager::BufferPoolManager(size_t pool_size,
        DiskManager* disk_manager,
        LogManager* log_manager)
        : sizeofpool(pool_size), disk_manager_(disk_manager),
        log_manager_(log_manager) {
        // a consecutive memory space for buffer pool
        pages_ = new Page[sizeofpool];
        page_table_ = new ExtendibleHash<page_id_t, Page*>(BUCKET_SIZE);
        replacer_ = new LRUReplacer<Page*>;
        free_list_ = new std::list<Page*>;

        // put all the pages into free list
        for (size_t i = 0; i < sizeofpool; ++i) {
            free_list_->push_back(&pages_[i]);
        }
    }

    /*
     * BufferPoolManager Deconstructor
     * WARNING: Do Not Edit This Function
     */
    BufferPoolManager::~BufferPoolManager() {
        delete[] pages_;
        delete page_table_;
        delete replacer_;
        delete free_list_;
    }

    /**
     * 1. search hash table.
     *  1.1 if exist, pin the page and return immediately
     *  1.2 if no exist, find a replacement entry from either free list or lru
     *      replacer. (NOTE: always find from free list first)
     * 2. If the entry chosen for replacement is dirty, write it back to disk.
     * 3. Delete the entry for the old page from the hash table and insert an
     * entry for the new page.
     * 4. Update page metadata, read page content from disk file and return page
     * pointer
     *
     * This function must mark the Page as pinned and remove its entry from LRUReplacer before it is returned to the caller.
     */
    //获取页
    Page* BufferPoolManager::FetchPage(page_id_t p_id) {
        //lock_guard<mutex> lck(latch_);
        Page* tg = nullptr;
        //首先遍历,如果有记录则返回
        if (page_table_->Find(p_id, tg)) { //1.1
            tg->pin_count_++;
            replacer_->Erase(tg);
            return tg;
        }
        //1.2
        //如果没有找到,那么就找到需要替代的页
        tg = GetVictimPage();
        if (tg == nullptr) return tg;
        //2
        //如果需要替换的页已被修改过,那么需要写回
        if (tg->is_dirty_) {
            disk_manager_->WritePage(tg->GetPageId(), tg->data_);
        }
        //3
        //去除需要去掉的页,在页表中插入需要获取的页
        page_table_->Remove(tg->GetPageId());
        page_table_->Insert(p_id, tg);
        //4
        //读取页并设置页的参数,以供后续的算法需要
        disk_manager_->ReadPage(p_id, tg->data_);
        tg->is_dirty_ = false;
        tg->page_id_ = p_id;
        tg->pin_count_ = 1;

        return tg;
    }
    //Page *BufferPoolManager::find

    /*
     * Implementation of unpin page
     * if pin_count>0, decrement it and if it becomes zero, put it back to
     * replacer if pin_count<=0 before this call, return false. is_dirty: set the
     * dirty flag of this page
     */
    bool BufferPoolManager::UnpinPage(page_id_t p_id, bool ifdir) {
        //lock_guard<mutex> lck(latch_);
        Page* tg = nullptr;
        page_table_->Find(p_id, tg);
        //如果未找到,返回false
        if (tg == nullptr) {
            return false;
        }
        tg->is_dirty_ = ifdir;
        //如果pincount<=0,返回false
        if (tg->GetPinCount() <= 0) {
            return false;
        }
        //如果减一后,pincout为0,那么把它放入replacer里面
        tg->pin_count_--;
        if (tg->pin_count_ == 0) {
            replacer_->Insert(tg);
        }
        return true;
    }

    /*
     * Used to flush a particular page of the buffer pool to disk. Should call the
     * write_page method of the disk manager
     * if page is not found in page table, return false
     * NOTE: make sure page_id != INVALID_PAGE_ID
     */
    //更新页的is_dirty_值,如果页未找到,那么返回false。如果找到,则把is_dirty_改为false,防止后面被错误替换
    bool BufferPoolManager::FlushPage(page_id_t p_id) {
        //lock_guard<mutex> lck(latch_);
        Page* tg = nullptr;
        page_table_->Find(p_id, tg);
        if (tg == nullptr || tg->page_id_ == INVALID_PAGE_ID) {
            return false;
        }
        if (tg->is_dirty_) {
            disk_manager_->WritePage(p_id, tg->GetData());
            tg->is_dirty_ = false;
        }

        return true;
    }

    /**
     * User should call this method for deleting a page. This routine will call
     * disk manager to deallocate the page. First, if page is found within page
     * table, buffer pool manager should be reponsible for removing this entry out
     * of page table, reseting page metadata and adding back to free list. Second,
     * call disk manager's DeallocatePage() method to delete from disk file. If
     * the page is found within page table, but pin_count != 0, return false
     */
    //删除页
    bool BufferPoolManager::DeletePage(page_id_t p_id) {
        //lock_guard<mutex> lck(latch_);
        Page* tg = nullptr;
        page_table_->Find(p_id, tg);
        if (tg != nullptr) {
            if (tg->GetPinCount() > 0) {//如果目标页的pincount>0,则不应该被删除
                return false;
            }
            //否则删除页
            page_table_->Remove(p_id);
            replacer_->Erase(tg);
            tg->is_dirty_ = false;
            tg->ResetMemory();
            free_list_->push_back(tg);
        }
        disk_manager_->DeallocatePage(p_id);
        return true;
    }

    Page* BufferPoolManager::GetVictimPage() {
        Page* tg = nullptr;
        if (!(free_list_->empty())) {
            tg = free_list_->front();
            free_list_->pop_front();
            if (tg->GetPageId() != INVALID_PAGE_ID)
            {
                printf("the target id is INVALID_PAGE_ID");
                exit(1);
            }
        }
        else {
            if (replacer_->Size() == 0) {
                return nullptr;
            }
            replacer_->Victim(tg);
        }
        if (tg->GetPinCount() != 0)
        {
            printf("the PinCount is zero!\n");
            exit(1);
        }
        return tg;
    }

    /**
     * User should call this method if needs to create a new page. This routine
     * will call disk manager to allocate a page.
     * Buffer pool manager should be responsible to choose a victim page either
     * from free list or lru replacer(NOTE: always choose from free list first),
     * update new page's metadata, zero out memory and add corresponding entry
     * into page table. return nullptr if all the pages in pool are pinned
     */
    Page* BufferPoolManager::NewPage(page_id_t& p_id) {
        //lock_guard<mutex> lck(latch_);
        Page* tg = nullptr;
        tg = GetVictimPage();
        if (tg == nullptr) return tg;

        p_id = disk_manager_->AllocatePage();
        //2
        //如果目标页被修改过
        if (tg->is_dirty_) {
            disk_manager_->WritePage(tg->GetPageId(), tg->data_);
        }
        //3
        //删除目标页,插入新页
        page_table_->Remove(tg->GetPageId());
        page_table_->Insert(p_id, tg);

        //4
        //修改相关参数
        tg->page_id_ = p_id;
        tg->ResetMemory();
        tg->pin_count_ = 1;
        tg->is_dirty_ = false;

        return tg;
    }



} // namespace cmudb

我们完成了所有的代码,测试结果如下:

 碰到的问题:

在参考答案中,很多的函数中,都在使用函数之前,进行了如下一句:

lock_guard<mutex> lock(latch);

但我自己写的时候,却并不知道为什么要这样,事实上在一些简单函数中也这样写的:

template <typename K, typename V>//返回全局深度
    int ExtendibleHash<K, V>::GetGlobalDepth() const {
        //lock_guard<mutex> lock(latch);
        return globalDepth;
    }

我自己做的时候进行了注释掉,但我不知道为什么标准代码会这样给出。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值