這裏就是須要實現迭代器的一些操做,好比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