【CMU 15-445】Proj2 Hash Index

EXTENDIBLE HASH INDEX

CMU-15445汇总

本文对应的project版本为CMU-Fall-2023的project2

由于Andy要求,本博客只提供思路,不会公开任何代码

本博客默认读者已懂可扩展哈希的插入删除理论知识,若不清楚流程可以看这篇博客

通关记录


目前的rank还比较低,后续会优化下

Task1 Read/Write Page Guards

Task1要求实现三种PageGuard类,分别是BasicPageGuard、ReadPageGuard和WritePageGuard。三种PageGuard类都使用RAII的思想保护Buffer Pool Manager的缓冲页,防止用户遗漏调用Unpin方法导致缓冲页被固定无法驱逐,与智能指针相似,当PageGuard对象生命周期结束时,在析构函数中调用Unpin方法来确保释放缓冲页;进一步的,ReadPageGuard和WritePageGuard还会保护缓存页的读写一致性,且避免死锁(若其他时候后忘记解锁,则在析构时解锁)。当然,PageGuard类也要对外提供方法用于手动释放。实现Task1中的类会为后面Task3的可扩展哈希做铺垫,后面用到的时候就知道多好用了。

三种类中的主要成员有:

  • BufferPoolManager *bpm_
  • Page *page_
  • bool is_dirty_

三种类中主要实现的方法有:

  • 三种PageGuard的移动构造函数、移动赋值运算符、Drop方法与析构函数
  • BasicPageGuard类中的UpgradeRead()函数和UpgradeWrite函数

在实现完三种PageGuard类中的方法后,我们需要使用这三种PageGuard,实现BufferPoolManager类中的FetchPageBasic、FetchPageRead、FetchPageWrite和NewPageGuarded函数。

移动构造函数

移动构造函数的实现比较简单,将参数中的成员复制到待构造对象,然后清空参数的所有成员即可。

Drop方法

Drop函数为对外提供的释放页接口,它需要做的事情就是调用一下BufferPoolManager的UnpinPage函数,将page_中维护的页释放(如果有的话);在ReadPageGuard和WritePageGuard中,则还需要解锁(读锁或写锁)

移动赋值运算符

移动赋值与移动构造差不多,不同点在于如果运算符左右两边的对象不同,需要将左边对象维护的缓冲页释放掉,并解开读锁或写锁,再将右边对象中的成员复制到左边,最后情况右边对象。

析构函数

调用Drop方法释放页并解锁即可。

UpgradeRead函数

利用BasicPageGuard中的成员构造出一个ReadPageGuard,并加写锁返回即可。UpgradeWrite同理。

FetchPageBasic、FetchPageRead、FetchPageWrite、NewPageGuarded

这几个方法的实现都比较简单,先调用bpm中对应的函数FetchPage或NewPage,然后构造BasicPageGuard或者ReadPageGuard或者WritePageGuard返回即可。值得注意的是在返回后两种之前需要先加RLatch或WLatch。

Task2 Extendible Hash Table Pages

Task2要求实现三种可扩展哈希中使用的数据结构,也就是三层结构中每一层的页面布局。分别是HeaderPage、DirectoryPage和BucketPage,如下图:

HeaderPage

HeaderPage的成员包括第二层DirectoryPage的PageId数组,以及一个位深度x,PageId数组的大小为 2 x 2^x 2x,且使用哈希值的最高有效x位进行索引。如位深度为2,32位哈希值为0x5f129982,最高有效位前2位为01,则会被索引到01对应的DirectoryPage上。

DirectoryPage

DirectoryPage的成员包含第三层的BucketPage的PageId数组,全局位深度global_depth和每一个Bucket的局部位深度local_depth数组,PageId数组的大小为 2 g l o b a l _ d e p t h 2^{global\_depth} 2global_depth,且使用哈希值的最低有效global_depth位进行索引。如全局位深度为2,32位哈希值为0x51129982,最低有效位后两位为10,则会被索引到10对应的BucketPage上。

BucketPage

BucketPage的成员包含一个Key-Value数组,以及数组大小size。被索引到该Bucket的Key-Value会追加到该Bucket上进行存储。

以上三个类的实现都比较简单,跟着头文件中的注释实现就是了。值得注意的是DirectoryPage的IncrGlobalDepth方法,在增加global_depth的同时还需要设置PageId数组中新增的PageId(具体见插入时的操作,见引言中的理论博客)

Task3 Extendible Hashing Implementation

Task3要求利用Task1和Task2中实现的类与方法,实现可扩展哈希类DiskExtendibleHashTable的方法,主要就是可扩展哈希的插入和删除。

理论知识见引言中的理论博客,大致跟着理论实现就行,需要注意的细节有:

  • 不需要使用的PageGuard需要及时释放,防止页面缓冲池满了
  • 关于插入,当Bucket满了导致分裂,可能分裂后的重新分配仍会产生Bucket满的情况,此时需要继续分裂,直到可以正常插入新tuple,我的实现方法是递归,也可以循环实现。
  • 关于删除,我删除空白页的方法是,每当删除一条tuple后,检查当前是否有空白的Bucket,如果有,判断global_depth与该Bucket的local_depth是否相等,相等则删除,不相等则后续缩短DirectoryPage之后再考虑;删除tuple导致的DirectoryPage缩短也需要循环的进行,直到无法再缩短为止。

Task4 Concurrency Control

Task4要求保证可扩展哈希的并发安全性,其实在Task3中如果FetchPageGuard和NewPageGuard使用正确的话,自然就保证并发安全性了,故不需要额外考虑这个Task。

相关推荐
lee_tianbai8 小时前
Java SSM 电影票预定系统|完整前后端项目,开箱即用(源码分享)
java·开发语言·数据库
SM_YHJ8 小时前
RFID通道门禁与资产台账联动的自动登记技术实现方案
大数据·数据库·人工智能
PaperData8 小时前
2014-2024年省级居民消费结构升级
数据库
砚底藏山河8 小时前
python量化入门:多周期数据对齐统一时间轴
java·数据库·python·金融·maven
Java后端的Ai之路8 小时前
昇腾NPU显存管理避坑指南:从npu-smi输出精准识别空闲卡的底层逻辑与实战
服务器·网络·数据库·npu·大模型服务
禾小西8 小时前
05丨Redis 内存快照:宕机后,如何实现快速恢复?
数据库·redis·缓存
笃行3508 小时前
数据同步软件:KFS 在 KingbaseES 项目里到底解决了什么
数据库
AOI小白新手上路9 小时前
视频去雨·论文蒸馏笔记:从物理先验到状态空间模型
数据库·笔记·音视频
小码哥0689 小时前
搭建短剧系统|海外短剧:从需求到上线的指南
网络·数据库·音视频·短剧小程序·短剧系统·短剧开发·短剧后台
HSunR9 小时前
ruoyi 若依 自定义注解 参数校验
java·前端·数据库