CMU15445 BufferPool Java版本
写在前面
一些话
对数据库存储引擎的结构比较感兴趣,所以看了CMU 15-445这门课,想要加深对存储引擎的理解,也想自己从零到一亲手实现一个项目,用实践检验自己的编程水平,同时可以丰富简历的内容。
本人主要学习的是Java这门语言,但这门课的lab是用C++实现的,很喜欢这门课,所以挑战一下用Java实现这些lab。
写这篇博客是为了整理思路,方便以后复习,互联网上用java写这个lab的项目寥寥无几,也希望可以为想用Java完成这门课的小伙伴提供一些思路。
本人水平很差,如有错误或更好的实现方法请务必在评论区指出来,不胜感激。
课程相关资源
我看的是2019年的课,实验做的是2020年的
课程视频可以在油管和B站找到,这里我推荐simniso这个团队,他们翻译得很好
实验一说明地址:Project #1 - Buffer Pool | CMU 15-445/645 :: Intro to Database Systems (Fall 2020)
实验官方Git hub地址:cmu-db/bustub: The BusTub Relational Database Management System (Educational) (github.com)
Buffer Pool缓冲池
课程视频里讲得很详细,可以配合《MySQL是怎样运行的》这本书的第17章食用
下面讲一下我理解的Buffer Pool的结构
- Buffer Pool由若干个页组成,这些页是磁盘中的页的复制版(称为缓冲页frame)
- 每一个缓冲页对应一个控制块(我称之为block)
- Buffer Pool中有若干条由控制块组成的链表:free链表,flush链表,LRU链表,……
具体这些结构是干嘛的,下面用到的时候再说吧
TASK #1 - LRU REPLACEMENT POLICY
任务描述:实现Buffer Pool中的LRU(Least Recently Used)链表
数据结构中的LRU
LRU链表原理没什么好说的,可以先去力扣做一下这道题:146. LRU 缓存 - 力扣(LeetCode)
Buffer Pool中的LRU
因为缓冲池的大小是有限的,所以在空间不够时,利用LRU结构将内存中不常用的页释放掉,为新加载的页腾出空间
思路
参考《MySQL是怎样运行的》第282页
当访问某个页时,可以按照下面的方式处理LRU链表:
- 如果该页不在Buffer Pool中,在把该页加载到缓冲池中时,把对应的控制块塞到LRU链表的头部
- 如果该页已经在Buffer Pool中,则直接把对应的控制块移动到LRU链表的头部
实验任务
Victim(T*)
: Remove the object that was accessed the least recently compared to all the elements being tracked by theReplacer
, store its contents in the output parameter and returnTrue
. If theReplacer
is empty returnFalse
.Pin(T)
: This method should be called after a page is pinned to a frame in theBufferPoolManager
. It should remove the frame containing the pinned page from theLRUReplacer
.Unpin(T)
: This method should be called when thepin_count
of a page becomes 0. This method should add the frame containing the unpinned page to theLRUReplacer
.Size()
: This method returns the number of frames that are currently in theLRUReplacer
.
Block
1 | import static common.Config.INVALID_PAGE_ID; |
1. Pin和Unpin
Pin:标记当前页正在被使用,从LRU中暂时移除该页对应的控制块
Unpin:当前页已经使用完毕,加到LRU头部
1 | public void pin(Block block) { |
题外话
为什么都是操作控制块block而不直接操作缓冲页frame呢?
个人理解如果不用控制块,而是把控制信息放在缓冲页中,等到把缓冲页刷回磁盘时,还要把这些无用的控制信息删掉,就很麻烦
所以用控制块保存控制信息,操作更简单,而且减少了每个缓冲页的额外数据的大小
而且控制块可以重复利用,同一个控制块不同时期可以对应不同的页
2. Victim函数实现
Victim是用来找到一个最不常用的缓冲页(LRU末尾的控制块对应的缓冲页)
1 | public Block victim() { |
size()没啥好说的就不说了
TASK #2 - BUFFER POOL MANAGER
Buffer Pool上面介绍过了
实验任务
FetchPageImpl(page_id)
NewPageImpl(page_id)
UnpinPageImpl(page_id, is_dirty)
FlushPageImpl(page_id)
DeletePageImpl(page_id)
FlushAllPagesImpl()
1. FlushPageImpl(page_id) & DeletePageImpl(page_id)
1 | //调用该方法时要确保pinCount == 0 |
2. NewPageImpl(page_id)
1 | //在buffer pool中新建一个页(磁盘中某个页的复制) |
3. FetchPageImpl(page_id)
1 | //获取指定的页 |
4. UnpinPageImpl(page_id, is_dirty)
1 | public void unpinPg(int pageId, boolean isDirty){ |
5. FlushAllPagesImpl()
1 | public void flushAllPg(){ |
初始化
1 | public BufferPoolManager(DiskManager diskManager, int bufferPoolSize) { |
参考
Project #1 - Buffer Pool | CMU 15-445/645 :: Intro to Database Systems (Fall 2020)
cmu-db/bustub: The BusTub Relational Database Management System (Educational) (github.com)
[已满分]CMU数据库(15-445)实验1-BufferPoolManager - 周小伦 - 博客园 (cnblogs.com)
LAOAWILLIAM/txDB: An on-disk relational database system (In progress) (github.com)
CNYuYang/BusTub: BusTub的Java ☕ 版本! (github.com)
《MySQL是怎样运行的》第17章