Overview
这次需要实现三个文件 可扩展哈希表,LRU-K BufferPool
这个project相较于之前难度上升了许多
其中 Extendible Hash Table 和 LRU-K Replacement Policy是作为bufferpool的组件使用的
Extendible Hash Table
一开始没有认真就之间写了,用的手法还是侯捷老师讲的那个散列表方法,直接一个vector存桶,然后桶的数量等于目录数量,目录扩容,然后重新分配,很神奇本地测试用例全对,结果线上一塌糊涂,没办法只能恶补一下知识了
这个hash table采用的是动态扩容的方法,有一个vector作为目录,其中内部类bucket作为桶存储key对应的value
桶的数量和目录的数量并不是一致的,首先明确几个概念 :
全局深度 global_depth_ 是目录的大小2的n次方倍
局部深度 local_depth 是bucket扩容的次数
IndexOf计算出来的值其实就是Hash值对目录大小做%的操作,只不过那里是写成&了
简介一下操作:
初始化时:global_depth_ 0 只有一个桶 local_depth_也是0
然后insert时当插不进去要看global_depth_ 是不是等于local_depth_如果是将目录扩大俩倍,然后扩大的部分挨个指向对应的桶,global_depth++ ,桶的深度++ , 然后就是桶分裂,桶数量++
然后有个注意的点就是:
当桶满了分裂的时候,是有可能还是分裂到一个桶的,然后这时候一个桶没数据,一个桶有数据,也得没数据的桶被连接到对应的vector中
LRU-K Replacement Policy
LRU-K的话推荐先学习一下LRU然后可以看我之前写的那个博客
LRU_K 对比LRU的话,其实就是将一个链表分成俩个链表,就是每访问一次其中的值对应的优先级是要++的,
如果优先级大于等于K那么就放入>=k的那个链表,这标志这我们以后可能很频繁的要访问他,每访问一次>=k的数据时需要记录一下他的时间搓,将来以后淘汰的话是要淘汰最久没访问过的数据的(<k的链表没有数据时或者他们都不可被驱逐时)
对于没有到达k次的,访问之后并不需要记录其时间搓
这个按照文档给出的来,也就不细说了
Buffer Pool Manager Instance
这个部分按照文档来也很容易,前提是仔细的看文档
对于该类介绍一下其成员
pages_ 是存储页的数组 每个页是一个基本单位在数据库中
page_id_ 是该页的编号,默认是buffer pool manager 分配
pin_count_ 是当前使用该页的进程\线程的数量
is_dirty_ 标记这该页有没有被修改
data_ 页中要存放的数据
frame_id_t 是pages_的下标相当于
page_table_ 负责映射frame_id和page_id之间的关系
disk_manager_ 是负责磁盘管理,数据写入写出之类的,提供好了只需要用其接口就可以
free_list_当前pages__空闲的部分
主要介绍一下FetchPgImp
FetchPgImp 要求获取一个页面的指针:
如果缓存中有那么直接获取到,然后Pin_count++ replacer_也要记录,并且设置不可驱逐
如果没有就要看有没有i空闲的frame_id,有的话,需要调用disk_manager_ 来read传入page的id的数据,并且 对该page做好初始化工作
如果没有的话要看当前有没有能驱逐的页面,有的话需要查看该页面是否是脏页面,脏的话需要写回磁盘然后 重置数据,同时也需要注意page_table_的映射关系,剩下和上面差不多
剩下的函数照着给出的讲义写就可以了,这里说一下,我之前没有仔细看英文直接翻译然后开写,然后翻译还不准导致我UnpinPgImp实现的时候,中文翻译的是pin_count为0直接删除掉,结果这个bug找好久,呜呜,最后看英文才知道是设置一下可驱逐就好了,真是悲哀
整个项目只有 FetchPgImp和NewPgImp和DeletePgImp代码比较多,不过照着仔细写就没问题,业务逻辑简单,没有扩展哈希麻烦,但是细节比较多
也可以来我的博客查看