【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。

相关推荐
ClickHouseDB8 小时前
ClickHouse Cloud 与 Snowflake 实时性价比对比:CostBench 测试中的 412 倍差距来
运维·数据库·系统架构
m0_639906948 小时前
Redis入门|五种数据结构、使用场景、持久化RDB&AOF
数据结构·数据库·redis
时间售卖铺9 小时前
触发器与函数实战:MySQL高效编程秘诀
运维·数据库·mysql·云原生
qetfw9 小时前
UOS MariaDB 数据库:安装、最小权限与备份验证
linux·数据库·mariadb·uos
数智园区合作社10 小时前
问数平台怎么把同一个指标锁成一个数
大数据·数据库·人工智能·ai
lbb 小魔仙11 小时前
Windows搭建PaddleOCR+飞书Webhook自动打卡通知机器人:识别、推送与远程触发完整教程
数据库·人工智能·prometheus·时序数据库
captain37612 小时前
MyBatis操作数据库(入门2)
java·数据库·spring boot·后端·spring·mybatis
心中有你021412 小时前
Java实现Dijkstra最短路径算法(邻接表 + 优先队列)
java·数据库·算法
龙亘川13 小时前
综合素质评价业务建模:从过程性评价到成长档案全链路闭环
数据库·智慧城市·数据可视化
数据库小组13 小时前
NineData 实战教程:MySQL 5.5 数据迁移同步到 MySQL 8.0(跨版本升级)
数据库·mysql·mysql优化·数据库开发·数据库迁移·版本升级·数据复制