2019 CMU 15-445 Project 1 Buffer Pool笔记与思路

题目要求

这次学15-445,就是填之前不好好学习的坑,都是泪。把lab的思路记录下来方便以后查阅,也是跟大家交流思想。言归正传,直接进入主题。

这次题目要求实现一个Buffer Pool,作用是为了减少磁盘访问次数。其中,Task 1 完成Clock Replacer替换算法,Task 2利用1来完成buffer pool

整体思路

Task 1: Clock Replacer

Clock Replacer置换算法

先说说Clock Replacer算法。思想很简单,就像钟表的指针一样一圈一圈遍历,直到找到符合替换的位置。文字描述过于复杂,直接利用图说明。

先做出如下假设,buffer的大小是3,访问的序列是1,2,3,1,4,3。

最初的三个元素1,2,3会直接加入到buffer中,如下表所示

num ref_bit clock_hand
1 1 <-
2 1
3 1

当访问到第二个1时,因为1在buffer中,直接读取。下面访问4,这时发现buffer中并没有4,那么就需要找一个位置替换并存入4。首先会将1处的ref_bit置0,并且指针下移一位,如下表。

num ref_bit clock_hand
1 0
2 1 <-
3 1

现在,clock_hand指向的2,而ref_bit仍然为1,则这一位也不能替换,那么就把对应的ref_bit置0,指针继续下移,如下表。

num ref_bit clock_hand
1 0
2 0
3 1 <-

同理,指针指向3遇到的情况与上次相同,因此继续执行相同操作。但是clock_hand到达边界,需返回到初始位置。便得到如下状态

num ref_bit clock_hand
1 0 <-
2 0
3 0

此时clock_hand指向的ref_bit为0,该位置能够被替换,替换1加入4,并把ref_bit置1,遍得到如下状态

num ref_bit clock_hand
4 1 <-
2 0
3 0

最后访问元素3,发现buffer中缓存有3,直接输出即可。

题目功能要求

题目要求完成四个函数,分别是Victim(),Pin(),Unpin(),Size()。这四个函数各有分工:

  • Victim():用来负责替换buffer pool中的元素。具体做法如下,从当前指针开始计算,如果需要读取的值在replacer中且指针指向的ref_bitTrue,就把它置为False,否则,就替换当前frame
  • Size():返回当前replacer中能被替换frame的数量。

Pin()Unpin()这两个函数单独拿出来仔细阐述一下,因为实在是困扰了我很久(真是技不如人,看得我云里雾里)。

实质上,执行Pin()Unpin()的都是buffer pool,如果buffer poolpage给pin了,说明这个page有进程访问,不能将它写回磁盘。而Unpin()则是有进程结束访问page的标志。执行Unpin()函数后,会发生以下两种情况:

  1. 没有进程访问page
  2. 仍然有进程访问page,即pin_count不为0。

对于情况1,该frame就可以通过调用Unpin()添加到Clock Replacer中,并将ref_bit置为False。而对于情况2,存有pageframe就不能被替换。

Pin()Victim()这两个函数非常相似,很多地方容易混淆。这里要说下Size()的具体含义,他表示能被替换的frame数量,Pin()Victim()都能让size减少。具体来说,Pin()就是buffer pool将某个pagepin住。如果这个pageUnpin()后还没来及替换又Pin()了,那这个page就不能被替换,所以size减少了。而Victim()就是选择一个frame进行替换。他把能够替换的frame替换掉了,那么可替换的frame数量当然会由此减少。

还有一点需要注意,只有Victim()才能更改clock_hand指针。

Task 2: Buffer Pool

Buffer Pool缓存磁盘中的page数据,其中每一个数据块叫做frame,如果frame被进程修改了,则称之为dirty page,必须将其写回磁盘后才能替换。这里列出具体函数的实现思路以供以后查阅复习,肯定有考虑不周全的地方,请各位大佬留言指正。

NewPageImpl(page_id)

这个函数是创建一个可用的page,下面是程序要求,这里不做赘述。

    // 0.   Make sure you call DiskManager::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.

在实现的过程中,我把上述要求调换了顺序,先进行1,2然后到0。直接判断是否全pin,否则从freelist中选择页面,如果freelist中没有可用的再使用Victim()找到需要替换的页。最后就是更新元数据等。

UnpinPageImpl()

如果该pagepin_countUnpin后为0才能加入到replacer中准备替换并返回true。其他如该页不存在、pin_count<=0等情况都返回false

FetchPageImpl()

该函数的参数为page_id,即buffer pool要使用本函数来寻找对应id的内容。那么如果在缓存区中国呢找到了,就直接读取且pin_count++,否则替换掉某一位置。如果buffer pool中没有能够替换的位置,则返回空指针。

剩余的函数由于没有测试用例,这里就不把它贴出来,免得今后误导自己。总之实现的方法都不难,搞清楚pageframe问题就能迎刃而解。

最后&疑问

这里还有很多不足的地方,比如没有考虑多线程。

在调试过程中发现unordered_map对一个空表查询或者删除映射,查找被删除的元素依然会返回0,这不得不让我把删除元素的 value 值设置为-1,但在其他程序中并不存在这样的问题。望各位能为我解惑。

你可能感兴趣的:(数据库系统概念)