第一题
1. 2025题目一:存储管理¶
题目种类:内核代码填充
推荐知识点:存储管理的基本原理、文件存储组织、记录存储组织、缓冲区管理
代码框架:https://gitlab.eduxiji.net/csc1/csc-db/db2025/-/tree/main/rmdb
** **
题目描述:
在大赛提供的代码框架的基础上,提供相关接口的实现,完成指定功能。本题目包含若干子任务,每个任务对应不同的测试点,只有通过相应测试点才可以获得相应的分数。
提示:
l 测试代码会调用指定接口来进行测试,参赛队伍不能修改已有的接口,也不能删除已有的数据结构及数据结构中的变量,但是可以增添新的接口、数据结构、变量。
l 大赛提供了项目结构文档来帮助参赛队伍理解代码框架,同时在代码注释中,也对数据结构及接口进行了说明,参赛队伍可以通过阅读代码注释来辅助理解代码框架。
1、磁盘管理器
在本任务中,参赛队伍需要实现磁盘管理器DiskManager的相关接口,磁盘管理器负责文件操作、读写页面等。在完成本任务之前,参赛队伍需要阅读项目结构文档中磁盘管理器的相关说明,以及代码框架中src/errors.h、src/storage/disk_manager.h、src/storage/disk_manager.cpp、src/common/config.h文件。
参赛队伍需要实现以下接口:
(1)void DiskManager::create_file(const std::string &path);
该接口的参数path为文件名,该接口的功能是创建文件,其文件名为path参数指定的文件名。
(2)void DiskManager::open_file(const std::string &path);
该接口的参数path为文件名,该接口的功能是打开文件名参数path指定的文件。
(3)void DiskManager::close_file(const std::string &path);
该接口的参数path为文件名,该接口的功能是关闭文件名参数path指定的文件。
(4)void DiskManager::destroy_file(const std::string &path);
该接口的参数path为文件名,该接口的功能是删除文件名参数path指定的文件。
(5)void DiskManager::write_page(int fd, page_id_t page_no, const char *offset, int num_bytes);
该接口负责在文件的指定页面写入指定长度的数据,该接口从指定页面的起始位置开始写入数据。
(6)void DiskManager::read_page(int fd, page_id_t page_no, char *offset, int num_bytes);
该接口需要从文件的指定页面读取指定长度的数据,该接口从指定页面的起始位置开始读取数据。
2、缓冲池管理器
在本任务中,参赛队伍需要实现缓冲池管理器BufferPoolManager和缓冲池替换策略Replacer相关的接口,缓冲池管理器负责管理缓冲池中的页面在内外存的交换,缓冲池替换策略主要负责缓冲区页面的淘汰和查找。在完成本任务之前,参赛队伍需要首先阅读项目结构文档中缓冲池管理器的相关说明,以及代码框架中src/storage和src/replacer文件夹下的代码文件。
对于缓冲池替换策略Replacer,参赛队伍需要实现一个Replacer的子类LRUReplacer,LRUReplacer实现了缓冲池页面替换的LRU策略,需要实现的接口如下:
(1) bool LRUReplacer::victim(frame_id_t* frame_id);
该接口的功能是选择并淘汰缓冲池中一个页面。如果成功找到要淘汰的页面,则函数返回值为true;否则,返回值为false。被淘汰的页面所在的帧由参数frame_id返回。
(2) void LRUReplacer::pin(frame_id_t frame_id);
该接口的功能是固定住一个帧中的页面,代表该页面正在使用,不可被换出,参数frame_id指定了帧的编号。
(3) void LRUReplacer::unpin(frame_id_t frame_id);
该接口的功能是取消固定一个帧中的页面,当该页面使用完毕时调用unpin函数取消对该页面的固定,参数frame_id指定了帧的编号。
对于缓冲池管理器BufferPoolManager,参赛队伍需要管理缓冲池中的页面,并对缓冲池进行并发控制,需要实现的接口如下:
(1) Page BufferPoolManager::new_page(PageId page_id);
该成员函数用于在缓冲池中申请创建一个新页面。如果创建新页面成功,则返回指向该页面的指针,同时通过参数page_id返回新建页面的编号。
(2) Page *BufferPoolManager::fetch_page(PageId page_id);
该成员函数用于获取缓冲池中的指定页面。待获取页面的编号由参数page_id给出。
(3) bool BufferPoolManager::find_victim_page(frame_id_t *frame_id);
当缓冲池中没有可用的空闲帧时,该成员函数用于寻找需要淘汰的页面。如果成功找到要淘汰的页面,则函数返回值为true;否则,返回值为false。被淘汰的页面所在的帧由参数frame_id返回。在实现这个成员函数时,需要调用LRUReplacer::victim函数。
(4) void BufferPoolManager::update_page(Page *page, PageId new_page_id, frame_id_t new_frame_id);
当缓冲池想要把某个帧中的页面置换为新页面或者删除该帧中的页面时,会调用update_page函数,该函数把指定帧中的原有页面刷入磁盘中,并将新页面和该帧建立映射,即更新页表。
(5) bool BufferPoolManager::unpin_page(PageId page_id, bool is_dirty);
当某个操作完成某个页面的使用之后,需要调用该函数将取消该操作对该页面的固定。当所有操作都完成该页面的使用之后,需要在Replacer中调用Unpin函数取消该页面的固定。
(6) bool BufferPoolManager::delete_page(PageId page_id);
用于在缓冲池中删除指定页面,同时将该页面所在的帧置为空闲帧。如果当前页面正在被某个操作使用,则该页面不能被删除。
(7) bool BufferPoolManager::flush_page(PageId page_id);
用于强制刷新缓冲池中的指定页面到磁盘上,无论该页是否正在被使用,或者是否为脏页,都需要把该页面的数据刷入磁盘中。
(8) void BufferPoolManager::flush_all_pages(int fd);
用于将指定文件中的存在于缓冲池的所有页面都刷新到磁盘。
提示:在缓冲池中,需要淘汰某个脏页时,需要将脏页写入磁盘。
3、记录管理器
在本任务中,参赛队伍需要填充记录管理器涉及的RmFileHandle类和RmScan类,RmFileHandle类负责对表的记录进行操作,RmScan类用于遍历表文件中存放的记录。
对于RmFileHandle类。在完成本文任务之前,参赛队伍需要阅读项目结构文档中记录管理器的相关说明,以及代码框架中src/record文件夹下的代码文件。
参赛队伍需要实现的接口如下:
(1) std::unique_ptr
该函数用于获取表中某一条指定位置的记录,每条记录由Rid来进行唯一标识。
(2) Rid RmFileHandle::insert_record(char buf, Context context);
该函数负责向表中插入一条新记录,在该函数中未指定记录的插入位置,参赛队伍需要选择一个空闲位置插入记录并同步更新表的元数据信息。
(3) void RmFileHandle::insert_record(const Rid &rid, char *buf);
该函数负责向表中的指定位置插入一条记录,该函数主要用于事务的回滚和系统故障恢复。
(4) void RmFileHandle::delete_record(const Rid &rid, Context *context);
该函数负责删除表中指定位置的记录。
(5) void RmFileHandle::update_record(const Rid &rid, char buf, Context context);
该函数负责把表中指定位置的记录更新为新的值。
对于RmScan类,参赛队伍需要实现的接口如下:
(1) RmScan(const RmFileHandle *file_handle);
该函数为RmScan类的构造函数,需要初始化相关成员变量。
(2) void RmScan::next() override;
该函数负责找到表文件中下一个存放了合法记录的位置。
(3) bool RmScan::is_end() const override;
该函数负责判断是否已经扫描到文件的末尾位置。
测试示例:
unit_test.cpp中提供了参考测试示例,最终测试包括但不限于unit_test.cpp中提供的测试。