这里就是须要实现迭代器的一些操做,好比begin、end、isend等等node
下面是对于IndexIterator
的构造函数c++
template <typename KeyType, typename ValueType, typename KeyComparator> IndexIterator<KeyType, ValueType, KeyComparator>:: IndexIterator(BPlusTreeLeafPage<KeyType, ValueType, KeyComparator> *leaf, int index_, BufferPoolManager *buff_pool_manager): leaf_(leaf), index_(index_), buff_pool_manager_(buff_pool_manager) {}
INDEX_TEMPLATE_ARGUMENTS INDEXITERATOR_TYPE BPLUSTREE_TYPE::Begin(const KeyType &key) { auto leaf = reinterpret_cast<BPlusTreeLeafPage<KeyType, ValueType,KeyComparator> *>(FindLeafPage(key, false)); int index = 0; if (leaf != nullptr) { index = leaf->KeyIndex(key, comparator_); } return IndexIterator<KeyType, ValueType, KeyComparator>(leaf, index, buffer_pool_manager_); }
nextPageId=-1
结束!=
和==
end
函数git
INDEX_TEMPLATE_ARGUMENTS INDEXITERATOR_TYPE BPLUSTREE_TYPE::end() { KeyType key{}; auto leaf= reinterpret_cast<BPlusTreeLeafPage<KeyType, ValueType,KeyComparator> *>( FindLeafPage(key, true)); page_id_t new_page; while(leaf->GetNextPageId()!=INVALID_PAGE_ID){ new_page=leaf->GetNextPageId(); leaf=reinterpret_cast<BPlusTreeLeafPage<KeyType, ValueType,KeyComparator> *>(buffer_pool_manager_->FetchPage(new_page)); } buffer_pool_manager_->UnpinPage(new_page,false); return IndexIterator<KeyType, ValueType, KeyComparator>(leaf, leaf->GetSize(), buffer_pool_manager_); }
==和 !=
函数github
bool operator==(const IndexIterator &itr) const { return this->index_==itr.index_&&this->leaf_==itr.leaf_; } bool operator!=(const IndexIterator &itr) const { return !this->operator==(itr); }
简单的index++而后设置nextPageId便可算法
template <typename KeyType, typename ValueType, typename KeyComparator> IndexIterator<KeyType, ValueType, KeyComparator> &IndexIterator<KeyType, ValueType, KeyComparator>:: operator++() { // // std::cout<<"++"<<std::endl; ++index_; if (index_ == leaf_->GetSize() && leaf_->GetNextPageId() != INVALID_PAGE_ID) { // first unpin leaf_, then get the next leaf page_id_t next_page_id = leaf_->GetNextPageId(); auto *page = buff_pool_manager_->FetchPage(next_page_id); if (page == nullptr) { throw Exception("all page are pinned while IndexIterator(operator++)"); } // first acquire next page, then release previous page page->RLatch(); buff_pool_manager_->FetchPage(leaf_->GetPageId())->RUnlatch(); buff_pool_manager_->UnpinPage(leaf_->GetPageId(), false); buff_pool_manager_->UnpinPage(leaf_->GetPageId(), false); auto next_leaf =reinterpret_cast<BPlusTreeLeafPage<KeyType, ValueType,KeyComparator> *>(page->GetData()); assert(next_leaf->IsLeafPage()); index_ = 0; leaf_ = next_leaf; } return *this; };
return array[index]便可数据库
template <typename KeyType, typename ValueType, typename KeyComparator> const MappingType &IndexIterator<KeyType, ValueType, KeyComparator>:: operator*() { if (isEnd()) { throw "IndexIterator: out of range"; } return leaf_->GetItem(index_); }
MaxReader
数就是为了防止等待的⌛️写进程饥饿首先来看若是没有🔒机制多线程会发生什么问题编程
就会出现下面的状况。❓安全
由此咱们须要读写🔒的存在多线程
因为咱们是只读操做,因此咱们到下一个结点的时候就能够释放上一个结点的Latch并发
剩下的操做都是同样的
delete
则不同由于咱们须要写操做
这里咱们不能释放结点A的Latch。由于咱们的删除操做可能会合并根节点。
到D的时候。咱们会发现D中的38删除以后不须要进行合并,因此对于A和B的写Write是能够安全释放了
Insert
操做这里咱们就能够安全的释放掉A的锁。由于B中还有空位,咱们插入是不会对A形成影响的
当咱们执行到D这里发现D中已经满了。因此此时咱们不会释放B的锁,由于咱们会对B进行写操做
上面的算法虽然是正确的可是有瓶颈问题。因为只有一个线程能够得到写Latch。而插入和删除的时候都须要对头结点加写Latch。因此多线程在有许多个插入或者删除操做的时候,性能就会大打折扣
这里要引入乐观🔒
乐观的假设大部分操做是不须要进行合并和分裂的。所以在咱们向下的时候都是读Latch而不是写Latch。只有在叶子结点才是write Latch
B-Link Tree简介
延迟更新父结点
这里用一个🌟来标记这里须要被更新可是尚未执行
这个时候咱们执行其余操做也是正确的好比查找31
这里咱们执行insert 33
当执行到结点C的时候。由于这个时候有另外一个线程持有了write Latch。因此这个时候🌟操做要执行。随后在插入33
最后一点补充关于扫描操做的
这时候会发生问题,由于线程2没法拿到read Latch
这里有几种解决方法
注意这里的Latch
和Lock
并不同
UnlockUnpinPages
的实现INDEX_TEMPLATE_ARGUMENTS void BPLUSTREE_TYPE:: UnlockUnpinPages(Operation op, Transaction *transaction) { if (transaction == nullptr) { return; } for (auto page:*transaction->GetPageSet()) { if (op == Operation::READ) { page->RUnlatch(); buffer_pool_manager_->UnpinPage(page->GetPageId(), false); } else { page->WUnlatch(); buffer_pool_manager_->UnpinPage(page->GetPageId(), true); } } transaction->GetPageSet()->clear(); for (const auto &page_id: *transaction->GetDeletedPageSet()) { buffer_pool_manager_->DeletePage(page_id); } transaction->GetDeletedPageSet()->clear(); // if root is locked, unlock it node_mutex_.unlock(); }
四个自带的解锁和上锁操做
/** Acquire the page write latch. */ inline void WLatch() { rwlatch_.WLock(); } /** Release the page write latch. */ inline void WUnlatch() { rwlatch_.WUnlock(); } /** Acquire the page read latch. */ inline void RLatch() { rwlatch_.RLock(); } /** Release the page read latch. */ inline void RUnlatch() { rwlatch_.RUnlock(); }
这里的rwlatch是本身实现的读写锁类下面来探究一下这个类
因为c++ 并发编程我如今还不太会。。。因此就简单看一下啦后面学完并发编程再补充
WLock
函数
writer_entered
表示是否有写操做void WLock() { std::unique_lock<mutex_t> latch(mutex_); while (writer_entered_) { reader_.wait(latch); } writer_entered_ = true; while (reader_count_ > 0) { writer_.wait(latch); } }
WunLock
函数
void WUnlock() { std::lock_guard<mutex_t> guard(mutex_); writer_entered_ = false; reader_.notify_all(); }
RLock
函数
由于是容许多个线程一块儿读这样并不会出错
void RLock() { std::unique_lock<mutex_t> latch(mutex_); while (writer_entered_ || reader_count_ == MAX_READERS) { reader_.wait(latch); } reader_count_++; }
RUnLatch
函数
void RUnlock() { std::lock_guard<mutex_t> guard(mutex_); reader_count_--; if (writer_entered_) { if (reader_count_ == 0) { writer_.notify_one(); } } else { if (reader_count_ == MAX_READERS - 1) { reader_.notify_one(); } } }
好了终于磕磕绊绊的写完了Lab2.关于数据库的Lab2应该会停一段时间。这段时间要补一补深度学习(毕竟要毕业)而后赶工一下老师给的活。同时学一下c++并发编程和看一下侯捷老师的课程。
最后附上GitHub的🔗
https://github.com/JayL-zxl/CMU15-445Lab