在线测试

本地测试

Project #1 - Buffer Pool

以下是Project #1的网址,2022FALL的Project #1是实现一个缓冲池。其中,为了实现缓冲池的管理,我们实现了缓冲池中的两个组件:可扩展哈希表和LRU-K 更换策略。最终利用可扩展哈希表来存储page与frame的对应关系,利用LRU-K 更换策略来实现frame的更新替换。而Buffer Pool Manager向系统提供了获得page的接口,只需要系统提供page_id就能够获得对应的page,而不需要关心如何获得。

1、可扩展哈希表

考虑到题目要求不能使用 built-in 的哈希表,我们需要自己实现可扩展哈希表用于维护Buffer Pool Manager中page与frame的对应关系。其中,Extendible Hash Table 由一个 directory 数组和多个 bucket 组成,其中 directory 数组的每一项都指向一个bucket :
directory:存放指向 bucket 的指针,是一个数组。用于寻找 key 对应 value 所在的 bucket。
bucket:存放 key 和 value,是一个链表。一个 bucket 可以至多存放指定数量的 key / value 对。

值得注意的是,在可扩展哈希表中,directory 数组的每一项都指向一个bucket,但一个bucket可以被多个directory 数组的项所指。 这主要与可扩展哈希表的插入机制有关:

  1. 当我们向哈希表中插入键值对 (K,V) 时,我们首先利用哈希函数计算出键值的哈希值std::hash<K>()(key)。而后我们利用哈希值计算出当前键值对应当储存的对应bucket,并将其放入相应位置。
  2. 在计算哈希值查找对应的bucket时,我们遵循如下的规则:我们提出全局深度 global depth ,其初始值为0,我们利用std::hash<K>()(key) & (1 << global_depth_) - 1,表示我们只使用哈希值的末 global depth 来确定对应的bucket位置。
  3. 但上述方法仍存在一定缺陷,当我们继续向当前的可扩展哈希表中插入元素,直至一个bucket已满无法继续插入新元素时,这是我们需要利用可扩展哈希表的机制,对当前的哈希表进行扩展。为了能够查找更多的bucket,我们需要增加 global depth 从而扩大识别bucket的范围。因此我们首先global depth++,这样做使得识别范围增大也会导致directory 容量翻倍。在此基础上,我们创建一个新的 bucket,并且需要重新安排原来 directory 对应 bucket 的指针。考虑到此时directory 的个数多于bucket的个数,因此往往此时会有多个directory 指向同一个bucket。为了实现上述的效果,我们针对每一个bucket 项单独设置一个local depth,当std::hash<K>()(key) & (1 << local depth ) - 1,我们让相应的directory 指向对应的bucket 。而后我们重新分配 KV 对,将已满的bucket 中的每一项通过local depth计算后放到相应的位置中。在这样实现了bucket的分裂之后,我们再向bucket插入新的键值对。

值得注意的是,在可扩展哈希表中,我们的global depth 始终大于等于 local depth。因此我们在实际实现代码中,我们主要需要分成三种情况进行讨论:1、当当前bucket不满时,我们直接插入键值对即可;2、当当前bucket已满时,若当前bucket的local depth等于global depth时,说明我们需要对directory 进行扩容,我们需要将directory 增长为原先的两倍;3、当当前bucket已满时,若当前bucket的local depth小于global depth时,说明我们不需要增长directory ,我们只需要增加bucket并重新分配指针和键值对即可。

总结:

  1. 考虑到多线程的使用,而且会有多个直线指向同一个bucket,我们使用make_shared指针;
  2. 使用resize函数会同时增加容器的size和capacity,而使用reserve只增加capacity,需要继续添加元素以免出现内存泄漏;
  3. 在判断已满的bucket中键值对应如何分配时,显然此时他们的末local depth都是相等的,我们可以直接根据末local depth+1位进行判断即可;
  4. 考虑到多线程进行操作,我们使用智能锁std::scoped_lock<std::mutex> lock(latch_);进行处理。

2、LRU-K 更换策略

LRU-K Replacer 用于存储 buffer pool 中 page 被引用记录,并根据记录选出buffer pool 满时需要被驱逐的 page。其中不同于传统的LRU策略,我们会对引用此时大于等于K次的page进行单独处理。当需要进行驱逐时,我们优先在引用次数小于K次的page中找出引用时间最早的进行驱逐;当所有page引用次数都大于等于K次时,我们在这些page中找到被引用时间最早的进行驱逐。

在进行代码实现时,我们添加了一个类FrameInfo用于记录当前Frame的信息,包括了其使用次数、可驱逐标志、ID等。而后我们设置了两个std::list<std::unique_ptr<FrameInfo>>列表分别用于记录使用次数大于K次的page和使用次数小于K次的page;并利用两个哈希表std::unordered_map<frame_id_t, std::list<std::unique_ptr<FrameInfo>>::iterator>来记录ID与FrameInfo之间的对应关系,同样按引用次数进行划分成两类。

  1. Evict函数中,我们首先在使用次数小于K次的page列表temp_pool_中进行查找,判断是否有page可驱逐,而后在使用次数大于等于K次的page列表cache_pool_中进行查找。在找到可驱逐项之后,需要同时删除列表和哈希表中的对应项;
  2. RecordAccess函数中,我们首先利用断言判断非法情况。而后我们同样在cache_pool_和temp_pool_中查找是否存在对应项,若存在我们先将其删除后,而后将其插入列表末尾,并更新哈希表中的指针,并增加相应项的使用次数;
  3. SetEvictable函数中,我们首先在temp_pool_和cache_pool_中查找对应项,若存在则更新其相应的可驱逐标志;
  4. Remove函数中,我们同样在temp_pool_和cache_pool_中查找对应项,同时需要判断相应项的可驱逐标志是否为true,否则需要抛出异常。在查找到之后需要在列表和哈希表中删除对应项;
  5. Size函数中,我们需要遍历整个temp_pool_和cache_pool_并统计所有可被驱逐的对象。

总结:

  1. 利用哈希表和列表来记录每一项,避免了对于时间戳和个数的讨论;
  2. 通过设计FrameInfo类,能够方便获取对应项的所有信息;
  3. 利用断言抛出异常。

3、缓冲池管理器实例

在实现了上述两个组件之后,我们可以在Buffer Pool Manager中直接调用我们设计好的函数,其中主要包括了几个对象:

  1. pages:存储 page 的指针数组;
  2. disk_manager_:向磁盘中读写文件;
  3. page_table_:任务一中的可扩展哈希表,记录page_id和frame_id的对应关系,即 page 在 buffer pool 中的位置;
  4. replacer_:任务二中的LRU-K 更换策略,用于更新page ;
  5. free_list_:记录当前缓冲池中为空的frame。

在进行代码实现时,我们采用如下的思路:

  1. NewPgImp函数中,我们首先判断当前缓冲池中是否有空闲的frame,若是则直接获取该frame;若不存在空闲frame,我们首先利用replacer_判断是否存在可驱逐的page :若不存在则直接返回nullptr;若存在我们根据IsDirty判断是否需要向磁盘中写文件,而后我们在page_table_中删除当前的page 与frame对应关系。而后我们利用AllocatePage函数新建page ,并重置更新原先frame的信息page_id_、pin_count_、is_dirty_,向page_table_中更新项,并调用RecordAccessSetEvictable表示当前frame被使用;
  2. FetchPgImp函数中,我们首先在page_table_查找是否能找对对应的page_id:若能则调用RecordAccessSetEvictable表示当前frame被使用,并更新其使用的进程数;否则我们进行讨论:1、若当前有空闲的frame,我们直接获取该frame;2、若不存在空闲frame,我们首先利用replacer_判断是否存在可驱逐的page :若不存在则直接返回nullptr;若存在我们根据IsDirty判断是否需要向磁盘中写文件,而后我们在page_table_中删除当前的page 与frame对应关系。而后我们更新当前frame中的信息page_id_、pin_count_、is_dirty_,向page_table_中更新项,并调用RecordAccessSetEvictable表示当前frame被使用;
  3. UnpinPgImp函数中,我们首先判断能否在page_table_中找到对应page_id,并判断其是否还有进程使用,否则返回false。而后我们减少进程数,并根据新的进程数判断是否需要修改可驱逐标志以及当前page是否为脏;
  4. FlushPgImp函数中,我们首先判断当前page_id是否有问题,而后我们根据page_id找到对应frame_id进行向磁盘中写文件,并更新is_dirty_;
  5. FlushAllPgsImp函数中,我们遍历当前pages_,首先判断当前page_id是否有问题,而后直接向磁盘中写文件并更新is_dirty_;
  6. DeletePgImp函数中,我们首先在page_table_中查找相应的page_id,并判断其对应的进程数。而后我们从page_table_和pages_中释放相应项,并利用replacer_删除相应的frame_id,最终将frame加入空闲列表中,并释放frame空间。

总结:

  1. 更新frame信息时需要注意与page的对应关系、进程数、脏标志、可驱逐标志;
  2. 根据脏标志判断是否需要写文件;
  3. 存在INVALID_PAGE_ID,需要针对写一个返回。

CMU15-445 Project.1总结相关推荐

  1. Activity生命周期方法的调用顺序project与測试日志

    以下为測试activity的方法的运行顺序   project与測试资源地址 androidproject AndroidManifest.xml <? xml version="1. ...

  2. WindowsForm如何实现类似微软project软件的甘特图?

    在管理软件研发过程中,特别是涉及项目管理或者生产计划方面,都需要一款类似微软project的控件对项目下的分解任务进行图形展示(甘特图).下面介绍一下在WindowsForm下如何实现类似微软proj ...

  3. 【IDEA】com.intellij.openapi.project.IndexNotReadyException

    [问题描述] 在IDEA中运行基于Maven构建Springboot项目时,报了如下的错误: com.intellij.openapi.project.IndexNotReadyException: ...

  4. ideal如何创建dynamic web project

    步骤如下 ① file -> new -> project ② 选择 Java Enterprise -> next ③ create project from template - ...

  5. This version of Android Studio cannot open this project, please retry with Android Studio 3.5 or new

    今天github 下载一个库 导入 as 提示 This version of Android Studio cannot open this project, please retry with A ...

  6. Error:The SDK Build Tools revision (23.0.3) is too low for project ':app'. Minimum required is 25.0.

    导入github上项目的时候出现 Error:The SDK Build Tools revision (23.0.3) is too low for project ':app'. Minimum ...

  7. Error:(49, 1) A problem occurred evaluating project ':guideview'. Could not read script 'https://r

    出现问题如下: Error:(49, 1) A problem occurred evaluating project ':guideview'. > Could not read script ...

  8. IntelliJ IDEA 的Project structure说明

    IntelliJ IDEA 的Project structure可以在File->Project structure中打开,同时,在新建项目是IDE一般用向导的方式让你填写Project str ...

  9. 将Project的内容导出成单独的XPO文件

    AX跟VSS整合的版本管理可以通过创建知识库将当前层的代码全部签入到VSS中,但是如果不是一个团队开发solution,而是针对客户的需求随时做得一些小改动,一般都希望以Project的形式组织代码和 ...

  10. linux vim project,vim插件project的用法

    用任何编辑器写代码,文件管理的方便与否对编码效率影响很大.一般的IDE都有文件管理功能,并且用来的不错.在vim中,要实现较好的文件管理功能一般都靠插件.在有米实习的第一个月,自己一直用NERDTre ...

最新文章

  1. SpringBoot 连接mysql踩到的坑
  2. 面试前临时抱佛脚——常见的Shell脚本面试题
  3. Python 全栈开发 -- 开发环境篇
  4. 静态初始化块的执行顺序
  5. Redis会遇到的15个「坑」,你踩过几个?
  6. Google, 请不要离开我们!
  7. linux java启动脚本文件_不错的linux下通用的java程序启动脚本
  8. 快学Scala习题解答—第三章 数组相关操作
  9. 达梦数据库导入oracle数据_达梦数据库助力加速石油石化行业数字化转型升级
  10. delphi接口带上请求头是什么意思_Python接口自动化之Token详解及应用
  11. 白板推导系列Pytorch-隐马尔可夫模型-概率计算问题
  12. php 浮点型能位运算,重读PHP手册笔记系列(二)
  13. 初始vue脚手架的项目文件中mian.js文件
  14. php 色彩空间转换,PHP Imagemagick将灰度转换为RGB
  15. 深圳招银java社招都有hr面吗,深圳招银网络科技
  16. 实现时间的计算: 要求用户输入身份证号,若格式有误,要求其重新输入。然后根据身份证号码输出20岁生日所在周的周三的日期
  17. 对今天知识的回顾15
  18. Python作业“骰子游戏”
  19. 机器学习初体验(傻瓜拖拽式)
  20. 21 间隙锁加锁规则

热门文章

  1. python+flask 配置https网站ssl安全认证
  2. 2023年武汉大学新闻与传播硕士考研上岸前辈备考经验指导
  3. 写一首关于学习的诗歌
  4. Spring Boot框架知识总结笔记(超详细,一次性到位)狂神说SpringBoot笔记
  5. mysql+主从复制+v_MySQL 的主从复制实践
  6. POI设置单个单元格的样式
  7. 盒子模型划分网页模块--音乐盒
  8. 朗朗与机器人合奏_咱厝“小郎朗”上《开学第一课》 与机器人PK弹奏
  9. Apache Nginx 防止盗链
  10. CSS :hover闪烁问题