CMU15-445 2022 Fall 通关记录 —— Project 2:B+ Tree(下篇)

Project 2:B+ Tree

Project #2 - B+Tree | CMU 15-445/645 :: Intro to Database Systems (Fall 2022)

NOTE:

记录完成该Pro中,一些可能会遇到的问题:

本实验中,有很多API是需要自己去实现的,因此,我推荐把逻辑梳理清楚后,再尝试去实现,函数设计是一门学问,我个人地原则是尽量避免出现重复代码段,即相同地处理置于一个函数中;

相较于Pro0和Pro1,本次指导书中的内容简略了许多,格外要注意到底是在哪实现代码,例如Task2是在src/include/storage/index/b_plus_tree.h和相应的CPP文件中实现(为什么会这么说?因为我就犯蠢了)

官方提供了一个可以正确打印实现的B+树的网页,可以用于检测与本地实现之间的差异,推荐在实现具体函数地时候可以先手动尝试一下插入、删除等操作。

BusTub B+Tree Printer (cmu.edu)

本次实验的实现中,涉及到了很多与迭代器相关的操作,包括但不限于std::prev\ std::next\ std::lower_bound \ std::move_backwrad(),所以我认为可以提前了解一下C++提供的这些语法糖,还能使你的code能力得到一定的提升。推荐网站:cppreference.com

每次用完Page后记得Unpin!

我在进行这次实验的时候,短暂地尝试了使用Copliot来辅助完成编写,可以省去很多简单逻辑的重复编写,但是有些细节逻辑处理的仍不是特别好,或者是有可能是我没有理解AI的意思。

虽然在我下文中地描述中使用了内部页和叶子页这种比较蹩脚地称呼,但是还是要意识到本质上依旧是树中地一个节点,为了方便,在代码中给变量取名地时候可以用page和node加以区分。

Task #3:Index Iterator

将叶子页迭代器化,近似于组织为链表(MySQL中就是如此),迭代器应当具有基本功能,例如运算符和for-each循环。

迭代器的操作仅需在src/include/storage/index/index_iterator.hsrc/index/storage/index_iterator.cpp中进行实现。

指导书中提供了以下几个成员函数,可以自行实现其他辅助函数

  • isEnd() :返回此迭代器是否指向最后一个键/值对。
  • operator++() :移至下一个键/值对。注意,形参为空的是前置++。
  • operator*() :返回该迭代器当前指向的键/值对。
  • operator==() :返回两个迭代器是否相等
  • operator!=() :返回两个迭代器是否不相等。

首先明确,迭代器中的元素应当为某个叶子页中的某元素,因此必然需要包含一个叶子页和当前叶子中的索引作为成员变量,以及为了能够在不同叶子页之间做到切换,也自然要把BufferPoolManager考虑进去,在发生切换时要记得Unpin前一个page。

本Task总体来说并不困难,相较于实现基础的B+树来说,就是做了一层封装。

但是实现完index_iterator中的内容后,不要忘记B+树中也是有对应的实现的(目的就是为了在B+树中使用):

image-20230701135457167

区别在于返回类型为迭代器类型。

Task #4:Concurrent Index

直接加一把大锁不是不可以,但是实验的初衷并非如此,由于项目中已经提供了页的锁,所以本Task的难点在于,何时、何处考虑加锁与解锁!

此处提供一篇详细讲述Task4的文章:

CMU 15445-2022 P2 B+Tree Concurrent Control - 知乎 (zhihu.com)

指导书中点明了一种名为latch crabbing的技术,基本思想是,遍历B+树的线程会在B+树页面上获取和释放锁(latch)。线程只能在子页面被认为是"安全的"时才能释放父页面上的锁。这里的"安全"的定义可以根据线程执行的具体操作而有所变化。在具体实现上,当一个线程需要访问一个页面时,它会首先获取该页面的锁。然后,线程会向上遍历树,逐级获取每个父页面的锁,直到达到根页面。在获取父页面的锁之前,线程需要判断其子页面是否被认为是"安全的",如果是,则可以释放子页面的锁,并继续向上获取父页面的锁。这种锁的获取和释放过程就像螃蟹在爬行时借助爪子一样,因此称为"latch crabbing"。

而“安全”的定义则是:节点在进行操作后,不会触发分裂或合并,影响父节点的指针。对插入操作,leafpage和internalpage的安全性应该分情况考虑,因为它们分裂的时机不同。对删除操作,节点要比minsize大。

这篇文章中还提到了一种测试多线程偶然出错的方式:

while xxxx/build/test/b_plus_tree_concurrent_test ; do echo 1; done;

指导书中对于加锁进行了简要的描述:

  • Search :从根页开始,抓住子页上的读(R)闩锁,然后在到达子页时立即释放父页上的闩锁。由于查询的性质是读,读锁是共享的。
  • Insert :从根页开始,抓住子页的写(W)锁存器。一旦子页被锁住,检查它是否安全,在这种情况下,不安全。如果子页安全,则释放对祖先的所有锁定。
  • Delete :从根页开始,抓住子页的写(W)锁存器。一旦子页被锁上,检查它是否安全,在这种情况下,至少是半满的。 (注意:对于根页,我们需要使用不同的标准进行检查)如果子页是安全的,则释放祖先上的所有锁。

有以下几个小点需要注意:

  • 有关锁的实现在src/include/common/rwlatch.h下。

    image-20230701214949439
  • 此前几乎没怎么被用到的transaction中也提供了遍历树时存储已获取的父节点锁的方法。

  • 要在Page上加锁,而非在节点上。

  • 需要在Leaf Page无法获取到兄弟页的锁时抛出异常以避免潜在的死锁。

  • 保护root_page_id在插入与删除时不会被并发更新(提示是使用std::mutex

无论是查找、插入还是删除,都需要通过FindLeaf函数找到对应的叶子页或者是对应的页,加锁的重点就在这个函数中实现

  • 如果是查找,FindLeaf时,每一次向下遍历都需要给子页加读锁,同时释放父页的读锁即可
  • 如果是插入,需要给遍历到的页加写锁,如果最终叶子页是安全的,既可以释放所有祖先的写锁
  • 如果是删除,与插入同理

在插入和删除的过程中,需要注意的是何时释放锁。

对于插入而言,有两个需要注意的点:

  • 如果造成分裂,此时待整个分裂过程完成前,叶子页和祖先的写锁是都不能被释放的,因为分裂时兄弟页的首索引需要上移至父页,父页也是有可能发生分裂的。
  • 当插入操作完成时,需要释放叶子页中的锁以及祖先上的所有锁

而对于移除而言,需要额外多提一个点:

  • 由于移除时涉及到借兄弟页元素的操作,因此也要记得给兄弟页加锁并且用完后即使释放。

测试记录

本地错误:b_plus_tree_insert_test 段错误

经测试,问题发生在如下代码段:

std::vector<int64_t> keys = {1, 2, 3, 4, 5};for (auto key : keys) {int64_t value = key & 0xFFFFFFFF;rid.Set(static_cast<int32_t>(key >> 32), value);index_key.SetFromInteger(key);tree.Insert(index_key, rid, transaction);}

当进行到第四次插入时,会发生错误如下:

Signal: SIGSEGV (Segmentation fault)

经定位,问题最终发生在InsertIntoParent函数的调用上,由于CheckPoint #1无需考虑transaction的问题,而InsertIntoParent本质上是一个递归的过程,在内部递归的该函数调用上缺少了transaction参量。

虽然这个问题看上去是一次疏忽,但是在排查过程中,也发现了另一个问题,即插入引起分裂后

本地错误:并发测试中的DeleteTest1未通过

当我把测试中的线程数改为1,就可以通过测试,说明问题确实发生在并发性上而非别的原因。

线上错误:

说实在的,没想到本地的测试通过后,线上依旧有这么多bug。唯一通过的测试案例是DeleteTest3

image-20230703195631812

看了一下输出的log,问题多在想要得到的值与实际得到的值不匹配,相比此前提交的一次多数超时的情况,已经好了很多了(此问题的起因源于IndexIterator在析构的时候,没有Unpin,主要也是因为我写完Task3之后,没有做insert和delete的本地测试)。

从头到尾检查了一番之后,发现出现了死锁问题:

问题可能发生在页的类型上,改日再进行排查。

编译指令&本地测试:

make b_plus_tree_insert_test -j4
make b_plus_tree_delete_test -j4
make b_plus_tree_contention_test -j4
make b_plus_tree_concurrent_test -j4./test/b_plus_tree_insert_test
./test/b_plus_tree_delete_test
./test/b_plus_tree_contention_test
./test/b_plus_tree_concurrent_test

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/13687.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

多元分类预测 | Matlab基于北方苍鹰优化深度置信网络(NGO-DBN)的分类预测,多输入模型,NGO-DBN分类预测

文章目录 效果一览文章概述部分源码参考资料效果一览 文章概述 多元分类预测 | Matlab基于北方苍鹰优化深度置信网络(NGO-DBN)的分类预测,多输入模型,NGO-DBN分类预测 多特征输入单输出的二分类及多分类模型。程序内注释详细,直接替换数据就可以用。程序语言为matlab,程序可…

4G理论概述

文章目录 LTE网络基础架构UMTS&#xff08;通用移动通信系统&#xff0c;Universal Mobile Telecommunications System&#xff09;UTRAN&#xff08;UMTS陆地无线接入网&#xff0c;UMTS Terrestrial Radio Access Network&#xff09;RNC&#xff08;无线网络控制器&#xff…

Quiz 14_2-1: Using Web Services | Python for Everybody 配套练习_解题记录

文章目录 Python for Everybody课程简介Quiz 14_2-1: Using Web Services单选题&#xff08;1-15&#xff09;操作题Autograder : Extracting Data from XML Python for Everybody 课程简介 Python for Everybody 零基础程序设计&#xff08;Python 入门&#xff09; This cou…

linux 命令之find

find 命令 1. 作用 命令用于在指定目录下以及其子母路查找文件和目录。 2. 语法 find [path] [expression] 不指定path的默认为当前路径 2.1 常见的参数 -name pattern&#xff1a;按文件名查找&#xff0c;支持使用通配符 * 和 ?。 -type type&#xff1a;按文件类型查…

【kubernetes系列】kubernetes之initcontainer初始化容器

概述 Init Container就是用来做初始化工作的容器&#xff0c;可以是一个或者多个&#xff0c;如果有多个的话&#xff0c;这些容器会按定义的顺序依次执行&#xff0c;只有所有的Init Container执行完后&#xff0c;主容器才会被启动。我们知道一个Pod里面的所有容器是共享数据…

<Java导出Excel> 4.0 Java实现Excel动态模板字段增删改查

思路&#xff1a; 主要是同时操作两张表&#xff1a;一张存储数据的表&#xff0c;一张存储模板字段的表&#xff1b; 查询&#xff1a;只查询模板字段的表&#xff1b; 新增&#xff0c;修改&#xff0c;删除&#xff1a;需要同时操作两张表中的字段 如果两张表字段不一致&…

【Mac】安装DbServer

1.下载 链接: https://pan.baidu.com/s/13Vzsv1sLAq-J8RsRgtL-pw 提取码: 8gsr 2.安装 如果提示已损坏&#xff0c;无法打开。 解决方式&#xff1a; 打开“终端”&#xff0c;输入命令&#xff1a;sudo spctl --master-disable&#xff0c;这个过程中会提示要输入密码&…

硬件电路设计--运算放大器(一)参数和分类

文章目录 前言一、运放分类1.1 功能分类1.2 按单颗IC封装1.3 第一脚的判断 二、运放参数2.1 理想运放2.2 实际运放2.3 数据手册中的重要参数2.3.1 供电电压Vs&#xff08;power supply&#xff09;2.3.2 虚短虚断2.3.3 输入偏置电流Ib2.3.4 噪声Vn2.3.5 静态电流IQ2.3.6 输入失…

【Distributed】分布式监控系统zabbix应用(一)

文章目录 前言一、Zabbix基本概述1. Zabbix 的概念2. Zabbix 的工作原理3. Zabbix 6.0 新特性3.1 Zabbix 可以实现高可用3.2 Zabbix 新增监控系统 4. Zabbix 的构成5. Zabbix 的功能组件5.1 数据库5.2 Web 界面5.3 Zabbix Agent5.4 Zabbix Proxy5.5 Java Gateway 6. Zabbix 和 …

Oracle19c默认用户名system密码不正确不能登录问题解决

Oracle19c默认用户名system密码不正确不能登录问题解决 1、oracle 命令乱码问题 oracle乱码问题一般是由于oracle字符集设置和操作系统字符集设置不一致造成的。 查看oracle字符集方式如下&#xff1a; 1.进入sqlplus 命令&#xff1a; sqlplus /nolog2.以系统管理员身份连…

20中文字符识别(matlab程序)

1.简述 随着计算机科学的飞速发展&#xff0c;以图像为主的多媒体信息迅速成为重要的信息传递媒介&#xff0c;在图像中&#xff0c;文字信息(如新闻标题等字幕) 包含了丰富的高层语义信息&#xff0c;提取出这些文字&#xff0c;对于图像高层语义的理解、索引和检索非常有帮助…

android studio 4.0以上隐藏调用方法参数名提示

引入&#xff1a; android studio在编辑代码的时候&#xff0c;调用函数时会接口处会自动提示参数名&#xff0c;方便代码书写时对传参命名的规范性。 可以如果代码是魂效过的&#xff0c;那会适得其反&#xff0c;l,l1,l2,i,i1,i2这样的参数名提醒反而会混淆视听。 这时候可…