cmu数据库project1 buffer pool

BUFFER POOL

完全新手向2023fall

task1 LRU-K replacement policy

  • 个人tip:首先可以先看看测试文件怎么写的(记得把DISABLE_去掉),先了解LRU是什么意思,然后再开写,只有一个锁,锁整个结构体所以在每个函数的开头直接用std::lock_guard<std::mutex> lock(latch_);
  • K:k 距离计算为当前时间戳与前 k 次访问的时间戳之间的时间差。如果没访问k次就是+inf,就是优先删除列表小于k的页;
  1. Evict(frame_id_t* frame_id):逐出,这个应该是整个最难的,其他都不难:
    • 循环map找到要删除的页,他的策略是优先history_.size()<k,页的编码最小,时间戳的时间最长;每次调用这个函数删除一个页,然后把这个页的id返回给传入参数frame_id;
  2. RecordAccess(frame_id_t frame_id):记录在当前时间戳访问给定帧 ID
    • 涉及到时间戳肯定是时间戳先加1,然后判断在不在map里,然后直接加;这里可以在Node类中写一个加时间戳的函数,逻辑就是**在链表头加一个时间戳,如果链表size>k就pop_back()**这样可以比较容易的访问第k个时间戳;
  3. Remove(frame_id_t frame_id):清除与帧关联的所有访问历史记录。
    • 这个就是在map里erase,重点是要维护curr_size_,不过我没写;
  4. SetEvictable(frame_id_t frame_id, bool set_evictable):改变页的状态
    • 这个就是直接改变;
  5. Size():记录有多少个要移除的节点;
    • 如果维护了curr_size_就直接返回,否则就是遍历一遍map,维护比较好,但测试用例比较少所以无所谓;

task2 Disk Scheduler磁盘调度

make disk_scheduler_test -j$(nproc)gdb ./test/disk_scheduler_test
目标:实现DiskScheduler 类,构造函数和析构函数已经写好了,只需要写Schedule(DiskRequest r)StartWorkerThread();这个task个人觉得也是比较容易;
Channel类提供了一个线程间共享数据的方法;src/include/common/channel.h
这个也不需要加锁因为channel里面维护了;

  1. Schedule(DiskRequest r):注意要使用std::move(),我觉得这就是包含一个类型转换的过程,Put函数在channel.h中已经写好了,Get函数也写好了,而且是自己维护的;
  2. StartWorkerThread():这个直接调用disk_manager_中写好的WritePage和ReadPage;

task3 BUFFER POOL MANAGER

这个有点难哈
首先Page结构存放page的信息很重要!page_这个数组存page
page_table_:通过page_id_找对应的帧,在这里帧就是bufferpool的格子,格子的大小就是pool_size_;BufferPoolManager是Page的友元可以直接访问私有成员;这里非常注意就是page_table_是page_id是key,frame是value;
pages_ = new Page[pool_size_];这里的pages_[0]就是第0号page的本体,返回Page*需要&pages_[0]

  1. NewPage(page_id_t* page_id):新建一个page,如果free list中有就去一个空的帧,如果没有就拿掉一个帧,用这个拿掉的帧,如果没有可以Evict的,就返回nullptr;
  • 第一步取frame_id_,如果有free就从freelist中取,没有就用replacer_移除一个,还是没有就return nullptr
  • AllocatePage()取一个新的页码
  auto pa = AllocatePage();  //新建一个page
  *page_id = pa;

  replacer_->RecordAccess(val);  // lru记录frame
  replacer_->SetEvictable(val, false);
  page_table_[pa] = val;
  pages_[val].page_id_ = pa;
  pages_[val].pin_count_ = 1;
  pages_[val].is_dirty_ = false;
  return &pages_[val];

一套流程
2. FetchPage(page_id_t page_id):这个和newpage比较相似,我觉得是最难写的

  • 如果已经在page_table里面了,就直接取;如果不在就需要,重复一下newpage的流程,先新建一个空的frame,把这个frame对应的所有东西清空,如果是脏页就需要flush;然后把这个page_id的内容写到frame上。
  auto promise1 = disk_scheduler_->CreatePromise();
  auto future1 = promise1.get_future();
  disk_scheduler_->Schedule({/*is_write=*/false, pages_[val].data_, /*page_id=*/page_id, std::move(promise1)});

要记得pages_[val].pin_count_++;
3. UnpinPage(page_id_t page_id, bool is_dirty):感觉这个比较容易

  • 如果没有这个页就返回flase,如果已经是pin_count_ == 0返回false;
  • 否则就是pin_count_--,如果是脏页就改状态,这里有一个问题就是,只需要改脏页情况;
  1. FlushPage(page_id_t page_id):disk_scheduler_主要用这个
if (pages_[val].is_dirty_) {
  auto promise1 = disk_scheduler_->CreatePromise();
  auto future1 = promise1.get_future();
  disk_scheduler_->Schedule({/*is_write=*/true, pages_[val].data_, /*page_id=*/page_id, std::move(promise1)});
  future1.get();//这个非常重要,如果没有这个就会出错;
  pages_[val].is_dirty_ = false;
  return true;
}

在本地测试中,由于一开始没加future.get()时而通过时而不通过
这是由于如果不加future.get(),他可能还没返回结果呢,就开始向下执行了;一定要记得加这个取回状态;(这个问题的解决办法是看知乎一位大佬的思路才明白)
5. DeletePage(page_id_t page_id):前面加一些题目里写的判断条件

page_table_.erase(page_id);
pages_[val].page_id_ = INVALID_PAGE_ID;
pages_[val].pin_count_ = 0;
pages_[val].is_dirty_ = false;
free_list_.push_back(val);
replacer_->Remove(val);
DeallocatePage(page_id);
return true;
  1. FlushAllPages():遍历一遍map调用flushpage;
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值