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

三种类中的主要成员有:

  • BufferPoolManager *bpm_
  • Page *page_
  • bool is_dirty_

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

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

在实现完三种PageGuard类中的方法后,我们需要使用这三种PageGuard,实现BufferPoolManager类中的FetchPageBasicFetchPageReadFetchPageWriteNewPageGuarded函数。

移动构造函数

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

Drop方法

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

移动赋值运算符

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

析构函数

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

UpgradeRead函数

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

FetchPageBasicFetchPageReadFetchPageWriteNewPageGuarded

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

Task2 Extendible Hash Table Pages

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

HeaderPage

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

DirectoryPage

DirectoryPage的成员包含第三层的BucketPagePageId数组,全局位深度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上进行存储。

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

Task3 Extendible Hashing Implementation

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

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

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

Task4 Concurrency Control

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

相关推荐
kejijianwen3 小时前
JdbcTemplate常用方法一览AG网页参数绑定与数据寻址实操
服务器·数据库·oracle
编程零零七3 小时前
Python数据分析工具(三):pymssql的用法
开发语言·前端·数据库·python·oracle·数据分析·pymssql
高兴就好(石6 小时前
DB-GPT部署和试用
数据库·gpt
这孩子叫逆6 小时前
6. 什么是MySQL的事务?如何在Java中使用Connection接口管理事务?
数据库·mysql
Karoku0666 小时前
【网站架构部署与优化】web服务与http协议
linux·运维·服务器·数据库·http·架构
码农郁郁久居人下7 小时前
Redis的配置与优化
数据库·redis·缓存
MuseLss8 小时前
Mycat搭建分库分表
数据库·mycat
Hsu_kk8 小时前
Redis 主从复制配置教程
数据库·redis·缓存
DieSnowK8 小时前
[Redis][环境配置]详细讲解
数据库·redis·分布式·缓存·环境配置·新手向·详细讲解
程序猿小D8 小时前
第二百三十五节 JPA教程 - JPA Lob列示例
java·数据库·windows·oracle·jdk·jpa