Project 1的主要目的是完成一个数据库的BufferPoolManager,并且将其分为了三小部分完成。首先是完成Extendible Hash Table。其实这部分内容在课程中讲的非常详细(为什么需要EHT,以及EHT的基本原理),并且实现部分只需要考虑扩容所以其实还算简单。
如图所示,在ETH中有个非常巧妙的想法是,通过控制hash值的有效位数来对整个hash表进行扩展和收缩。具体表现为:1. global代表全局的hash值的有效位数,当某个bucket满了以后并且global == local时,通过他来扩大全局索引的大小(翻倍)。2.local表示bucket上hash值的有效位置,当bucket满了以后通过他来redistribute bucket上值的位置。具体操作为:当某个新插入的值对应的bucket满了以后,1. 如果local == global则需要将global翻倍并redistribute bucket当中的值直到对应的bucket有足够的空间来容纳新的值。2. 如果local < global,则可以直接redistribute bucket当中的值并将local++,直到对应的bucket有足够的空间来容纳新的值。
部分实现代码:
1 |
|
第二部分实现一个LRU算法,实现方式多样且简单,在此略过。
第三部分,借助之前实现好的ETH和LRU,来做一个简单的BufferPoolManager,如果能够理解实验代码给出的整体架构事情实现起来也是非常的简单。
如图所示:
整个Buffer Pool Manager分为5个部分,其中disk部分给出的实验代码已经帮你实现好了。内存部分则对应代码中的pages_,一开始就指定好了大小,并且初始化的时候将pages都添加到free_list当中去。而当Fetch Data的时候需要先查看Data在不在Hashtable中,如果在则直接从内存中读取,否则需要先从Disk中读到内存并且将其添加到hashtable中去。通常情况想因为Memory«Disk所以当内存没满的时候(free_list > 0)可以直接从free_list中获取空闲内存。但是当内存满了以后(free_list == 0)怎需要替换出暂时不用的(Unpinned)的内存空间即从replace中释放新的内存。在释放新的内存的过程中需要考虑内存中的内容是否发生了改变(isdirty),并确定在释放内存之前是否要将里面的内容先回写到磁盘。
部分实现代码:
1 |
|
实验环境的搭建注意:
博主使用的g++的版本是5,如果用的g++版本过高的话会出现: -Werror=uninitialized错误