/\*\* Array of buffer pool pages. \*/ Page \*pages_; /\*\* Pointer to the disk manager. \*/ DiskManager \*disk_manager_ __attribute__((__unused__)); /\*\* Pointer to the log manager. \*/ LogManager \*log_manager_ __attribute__((__unused__)); /\*\* Page table for keeping track of buffer pool pages. \*/ std::unordered_map<page_id_t, frame_id_t> page_table_; /\*\* Replacer to find unpinned pages for replacement. \*/ Replacer \*replacer_; /\*\* List of free pages. \*/ std::list<frame_id_t> free_list_; /\*\* This latch protects shared data structures. We recommend updating this comment to describe what it protects. \*/ std::mutex latch_;
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. frame_id_t fid; Page\* p = nullptr; // step 1.1. if(page_table_.find(page_id) != page_table_.end()){ fid = page_table_[page_id]; pages_[fid].pin_count_++; // pin it. replacer_->Pin(fid); return &pages_[fid]; } latch_.lock(); // step 1.2. if (free_list_.empty()) { // pick from free list first. //can't find a page that can be flushed. if (!replacer_->Victim(&fid)) { return nullptr; } else { // the page placed in the lrureplacer need to be flushed. // send it to the disk p = &pages_[fid]; if (p->is_dirty_) { disk_manager_->WritePage(p->page_id_, p->data_); p->is_dirty_ = false; } // erase the pagetable_. page_table_.erase(p->page_id_); } } else { //found: fid = free_list_.front(); free_list_.pop_front(); p = &pages_[fid]; } // Step 3. page_table_[page_id] = fid; replacer_->Pin(fid); // Step 4. p->page_id_ = page_id; p->pin_count_ = 1; disk_manager_->ReadPage(page_id, p->data_); latch_.unlock(); return p; }
bool BufferPoolManagerInstance::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. if (page_table_.find(page_id) == page_table_.end()) { return true; } // P does exists. frame_id_t fid = page_table_[page_id]; Page\* deletepage = &pages_[fid]; // P is pinned if (deletepage->pin_count_ != 0) { return false; } // if dirty rewrite: // flush the page before deallocate it. latch_.lock(); if (deletepage->is_dirty_) { disk_manager_->WritePage(page_id, deletepage->data_); deletepage->is_dirty_ = false; } DeallocatePage(page_id); // remove P from the page table. page_table_.erase(page_id); // reset its metadata. // the page returned to freelist does not stores any page. deletepage->page_id_ = INVALID_PAGE_ID; deletepage->is_dirty_ = false; deletepage->pin_count_ = 0; // return it to free_list_. free_list_.push_back(fid); latch_.unlock(); return true; }
好了,最难的几个函数我们都做完了,剩下的几个都比较简单.
4、FlushPgImp函数
刷新一个页,把这个页里面的数据写入磁盘.
找到这个页,然后调用WritePage就可以了.
1 2 3 4 5 6 7 8
bool BufferPoolManagerInstance::FlushPgImp(page_id_t page_id) { // Make sure you call DiskManager::WritePage! std::lock_guard<std::mutex> lock(latch_); if (page_table_.find(page_id) == page_table_.end() page_id == INVALID_PAGE_ID) return false; disk_manager_->WritePage(page_id, pages_[page_table_[page_id]].data_); return true; }
5、UnpinPgImp函数
让一个页的pin数减1.
找到一个页,首先置脏位,然后让pin数-1,如果减为0就调用Replacer的Unpin函数.
1 2 3 4 5 6 7 8 9 10 11 12 13 14
bool BufferPoolManagerInstance::UnpinPgImp(page_id_t page_id, bool is_dirty) { std::lock_guard<std::mutex> lock(latch_); frame_id_t fid = page_table_[page_id]; Page\* p = &pages_[fid]; p->is_dirty_ = is_dirty; // hold the state until victim. if (p->pin_count_ <= 0) return false; --p->pin_count_; if (p->pin_count_ == 0) { replacer_->Unpin(fid); return true; } return false; }
Page \*ParallelBufferPoolManager::NewPgImp(page_id_t \*page_id) { // create new page. We will request page allocation in a round robin manner from the underlying // BufferPoolManagerInstances // 1. From a starting index of the BPMIs, call NewPageImpl until either 1) success and return 2) looped around to starting index and return nullptr // 2. Bump the starting index (mod number of instances) to start search at a different BPMI each time this function is called for(size_t i = 0; i < num_instances_; i++){ BufferPoolManager\* one_manager_instance = manager_[next_instacnce_]; Page \*page = one_manager_instance->NewPage(page_id); if(page != nullptr){ return page; } next_instacnce_ = (next_instacnce_ + 1) % num_instances_; } return nullptr; }
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 manager_[page_id % num_instances_]; }
Page \*ParallelBufferPoolManager::FetchPgImp(page_id_t page_id) { // Fetch page for page_id from responsible BufferPoolManagerInstance return GetBufferPoolManager(page_id)->FetchPage(page_id); }