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 theReplaceris 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_countof 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章