BufferPoolManagerInstance::BufferPoolManagerInstance(size_t pool_size, uint32_t num_instances, uint32_t instance_index, DiskManager *disk_manager, LogManager *log_manager) : pool_size_(pool_size), num_instances_(num_instances), instance_index_(instance_index), next_page_id_(static_cast<page_id_t>(instance_index)), disk_manager_(disk_manager), log_manager_(log_manager) { BUSTUB_ASSERT(num_instances > 0, "If BPI is not part of a pool, then the pool size should just be 1"); BUSTUB_ASSERT( instance_index < num_instances, "BPI index cannot be greater than the number of BPIs in the pool. In non-parallel case, index should just be 1."); // We allocate a consecutive memory space for the buffer pool. pages_ = new Page[pool_size_]; replacer_ = newLRUReplacer(pool_size);
// Initially, every page is in the free list. for (size_t i = 0; i < pool_size_; ++i) { free_list_.emplace_back(static_cast<int>(i)); } }
/** * Flushes the target page to disk. * 刷新目标页到磁盘 * @param page_id id of page to be flushed, cannot be INVALID_PAGE_ID * @return false if the page could not be found in the page table, true otherwise */ boolBufferPoolManagerInstance::FlushPgImp(page_id_t page_id){ // Make sure you call DiskManager::WritePage! std::scoped_lock lock{latch_}; if (page_id == INVALID_PAGE_ID) { returnfalse; } auto iter = page_table_.find(page_id); // 如果page不在页表中 if (iter == page_table_.end()) { returnfalse; } // 在页表中找到frameid,然后根据他获取Page对象 frame_id_t frame_id = iter->second; Page *page = &pages_[frame_id]; disk_manager_->WritePage(page_id, page->GetData()); page->is_dirty_ = false; // 刷新之后重置dirty状态 returntrue; }
/** * Flushes all the pages in the buffer pool to disk. * 刷新buffer pool中的所有页到磁盘 */ voidBufferPoolManagerInstance::FlushAllPgsImp(){ // You can do it! std::scoped_lock lock{latch_}; auto iter = page_table_.begin(); while (iter != page_table_.end()) { page_id_t page_id = iter->first; frame_id_t frame_id = iter->second; Page *page = &pages_[frame_id]; disk_manager_->WritePage(page_id, page->GetData()); iter++; } }
/** * Creates a new page in the buffer pool. * 在缓冲池中创建一个新页面 * @param[out] page_id id of created page * @return nullptr if no new pages could be created, otherwise pointer to new page */ Page *BufferPoolManagerInstance::NewPgImp(page_id_t *page_id){ // 0. Make sure you call AllocatePage! // 1. If all the pages in the buffer pool are pinned, return nullptr. // 2. Pick a victim page P from either the free list or the replacer. Always pick from the free list first. // 3. Update P's metadata, zero out memory and add P to the page table. // 4. Set the page ID output parameter. Return a pointer to P. std::scoped_lock lock{latch_}; // 优先从freelist里获取空页,没有就去replacer中找一个,如果都找不到就返回nullptr frame_id_t frame_id = -1; Page *page = nullptr; if (!free_list_.empty()) { frame_id = free_list_.front(); free_list_.pop_front(); page = &pages_[frame_id]; } elseif (replacer_->Victim(&frame_id)) { page = &pages_[frame_id]; // 从replacer中找到的页面要从pagetable中先删除 if (page->IsDirty()) { disk_manager_->WritePage(page->GetPageId(), page->GetData()); } page_table_.erase(page->GetPageId()); } else { returnnullptr; }
/** * Fetch the requested page from the buffer pool. * 从缓冲池中获取请求的页面 * @param page_id id of page to be fetched * @return the requested page */ Page *BufferPoolManagerInstance::FetchPgImp(page_id_t page_id){ // 1. Search the page table for the requested page (P). // 1.1 If P exists, pin it and return it immediately. // 1.2 If P does not exist, find a replacement page (R) from either the free list or the replacer. // Note that pages are always found from the free list first. // 2. If R is dirty, write it back to the disk. // 3. Delete R from the page table and insert P. // 4. Update P's metadata, read in the page content from disk, and then return a pointer to P. std::scoped_lock lock{latch_}; auto iter = page_table_.find(page_id); // 如果存在,即Page在缓冲池中 if (iter != page_table_.end()) { // 找到这个页面,Pin它并返回它 frame_id_t frame_id = iter->second; Page *page = &pages_[frame_id]; page->pin_count_++; replacer_->Pin(frame_id); return page; } // 不存在,即Page在磁盘中 // 优先从freelist里获取空页,没有就去replacer中找一个,如果都找不到就返回nullptr frame_id_t frame_id = -1; Page *page = nullptr; if (!free_list_.empty()) { frame_id = free_list_.front(); free_list_.pop_front(); page = &pages_[frame_id]; } elseif (replacer_->Victim(&frame_id)) { page = &pages_[frame_id]; // 从replacer中找到的页面要从pagetable中先删除 if (page->IsDirty()) { disk_manager_->WritePage(page->GetPageId(), page->GetData()); } page_table_.erase(page->GetPageId()); } else { returnnullptr; }
/** * Deletes a page from the buffer pool. * 从缓冲池中删除一个页面 * @param page_id id of page to be deleted * @return false if the page exists but could not be deleted, true if the page didn't exist or deletion succeeded */ boolBufferPoolManagerInstance::DeletePgImp(page_id_t page_id){ // 0. Make sure you call DeallocatePage! // 1. Search the page table for the requested page (P). // 1. If P does not exist, return true. // 2. If P exists, but has a non-zero pin-count, return false. Someone is using the page. // 3. Otherwise, P can be deleted. Remove P from the page table, reset its metadata and return it to the free list. std::scoped_lock lock{latch_}; DeallocatePage(page_id); auto iter = page_table_.find(page_id); // 如果不存在,即Page在磁盘中,直接返回成功 if (iter == page_table_.end()) { // DeallocatePage(page_id); returntrue; } // 如果存在,即Page在缓冲池中 frame_id_t frame_id = iter->second; Page *page = &pages_[frame_id]; // 如果PinCount不为0,则说明有人在用,返回失败 if (page->GetPinCount() > 0) { returnfalse; } // 清理,更新元数据,删除pagetable,返还至freelist if (page->IsDirty()) { disk_manager_->WritePage(page->GetPageId(), page->GetData()); } replacer_->Pin(frame_id); page_table_.erase(page->page_id_); page->is_dirty_ = false; page->pin_count_ = 0; page->page_id_ = INVALID_PAGE_ID; page->ResetMemory(); free_list_.push_back(frame_id); // DeallocatePage(page_id); returntrue; }
/** * Unpin the target page from the buffer pool. * 在缓冲池中Unpin一个页面 * @param page_id id of page to be unpinned * @param is_dirty true if the page should be marked as dirty, false otherwise * @return false if the page pin count is <= 0 before this call, true otherwise */ boolBufferPoolManagerInstance::UnpinPgImp(page_id_t page_id, bool is_dirty){ std::scoped_lock lock{latch_}; auto iter = page_table_.find(page_id); // 如果不存在,即Page在磁盘中,直接返回true if (iter == page_table_.end()) { returnfalse; } // 如果存在,即Page在缓冲池中 frame_id_t frame_id = iter->second; Page *page = &pages_[frame_id]; if (page->GetPinCount() <= 0) { // 如果没被pin过,直接返回false returnfalse; } page->pin_count_--; if (is_dirty) { // 判断而非直接赋值是为了避免覆盖以前的状态 page->is_dirty_ = true; } if (page->GetPinCount() <= 0) { replacer_->Unpin(frame_id); } returntrue; }
// buffer/parallel_buffer_pool_manager.cpp ParallelBufferPoolManager::ParallelBufferPoolManager(size_t num_instances, size_t pool_size, DiskManager *disk_manager, LogManager *log_manager) { // Allocate and create individual BufferPoolManagerInstances num_instances_ = num_instances; pool_size_ = pool_size; next_instance_ = 0; for (size_t i = 0; i < num_instances_; i++) { managers_.push_back(newBufferPoolManagerInstance(pool_size, num_instances_, i, disk_manager, log_manager)); } }
析构函数要记得删除这些对象
1 2 3 4 5 6
// buffer/parallel_buffer_pool_manager.cpp ParallelBufferPoolManager::~ParallelBufferPoolManager() { for (size_t i = 0; i < num_instances_; i++) { delete managers_[i]; } }
然后需要关注下GetBufferPoolManager这个函数,其实就是直接取模即可:
1 2 3 4 5
// buffer/parallel_buffer_pool_manager.cpp BufferPoolManager *ParallelBufferPoolManager::GetBufferPoolManager(page_id_t page_id){ // Get BufferPoolManager responsible for handling given page id. You can use this method in your other methods. return managers_[page_id % num_instances_]; }