cmu 15445 22fall p2 bplus tree checkpoint2 concurrency

本文围绕C++实现B+树的实验展开,介绍了task3迭代器的实现思路,即找到指定key值所在叶节点并具备cpp迭代器功能。还阐述了task4并发操作,对比了螃蟹锁和乐观锁,给出乐观锁写操作思路。同时说明了实现细节,如记录上锁父节点、处理根节点锁等。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在这里插入图片描述
debug麻了的一个实验,中途不知道多少次要放弃(多线程并发是真的难绷

感谢知乎上和一些博客网站上的大佬分享的经验,不然现在也发布不了这篇博客了/dog

task3 iterator

迭代器要求我们实现一个找到指定key值的所在叶节点,并且能够像cpp的迭代器的功能一样。
思路:先用findleafpage找到包含该key值的叶子节点,作为迭代器的初始化参数,在迭代器中添加一个LeafPage指针存储迭代器当前所处的叶节点,以及一个整形变量存放当前叶节点数组遍历到哪个元素了,当下标等于maxsize即可用fetch 当前节点的nextpageid出来,同时记得unpin和解锁之前的节点。

task4 concurrency

课程教的是螃蟹锁(前脚跟着地后脚跟离地),从根节点开始,先把父节点上锁,然后往下遍历找到目标孩子节点,如果孩子节点处于安全状态,插入操作时安全状态是小于maxsize(或小于等于,跟你的节点分裂时机实现一样),删除操作时则是大于minsize,则可以把父节点的锁撤掉,孩子节点上锁,接着往下遍历;重复上述操作直到找到目标节点。

当然,如果只是读取操作的话就无需判断孩子节点是否为安全状态,往下走一步立马撤走后脚跟,即父节点的锁同时unpin父节点。

螃蟹锁是悲观锁,写操作无论是否造成b+树结构变化,在遍历的过程中都会对根节点加写锁,性能非常差。

所以我们可以采用乐观锁的方式来实现写操作。具体思路是:一开始采用读锁的形式对父节点加锁,只要往下遍历的时候孩子节点总是处于安全状态,就可以不断地对父节点撤读锁对孩子节点加读锁直到找到叶节点时加上写锁即可;

如果在遍历的过程中出现了孩子节点不安全的情况,才改为上写锁。

这样一来,一开始对根节点的访问都是上共享读锁,不会阻塞多个线程对根节点的访问操作。

实现细节

1.在寻找目标叶节点的过程中,当出现节点不安全状态时,需要记录沿途的上锁父节点,可以用Transaction中的pageset,当然,存储在set中的page一定不能提前unpin,否则该page可能已经被替换过了,然后在完成好节点的更新操作后遍历pageset对其一一解锁和unpin(不需要提前解锁,因为获取完需要更新的所有节点锁后,进行插入或删除操作时都是从下往上遍历的,即使提前释放下面的锁也用不了)。

2.由于根节点没有父节点能够上锁保护,所以需要另外设置一个专供根节点使用的读写锁,也就是维护root_page_id_,只有在root_page_id_需要读或修改时才上锁,用完立刻释放。
同时,要注意一种情况,两个线程同时读了root_page_id_,其中一个线程造成了根节点的更新操作(生成新的节点或者旧根节点删除),此时阻塞在获取原根结点锁步骤的线程会不知道根节点已经被替换导致出现错误。所以,在获取完根节点的锁之后,还需要判断该节点是否为根节点,如果发现不为根节点,则循环读取root_page_id_,上锁,判断是否为根节点三个步骤直到获取到正确的根节点(这个过程,fetch完page后如果发现不是正确的根节点时,注意unpin掉原根节点)。

3.删除操作需要删除的节点页可以同样使用Transaction中的deletepageset,最后完成删除操作后再进行DeletePage。

4.对于只需要修改父节点指针的目标节点,是不需要进行上锁的。
然后最头痛的地方就在于加锁时机以及遗漏一些页进行unpin,会导致缓冲区满全部页面被锁上,fetch出空页的情况。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值