在DBImpl中有一個函數聲明為Iterator* DBImpl::NewIterator(const ReadOptions& options) ,他返回一個可以遍歷或者搜索數據庫的迭代器句柄。
Iterator* DBImpl::NewIterator(const ReadOptions& options) { SequenceNumber latest_snapshot; uint32_t seed; Iterator* iter = NewInternalIterator(options, &latest_snapshot, &seed); return NewDBIterator( this, user_comparator(), iter, (options.snapshot != NULL ? reinterpret_cast<const SnapshotImpl*>(options.snapshot)->number_ : latest_snapshot),
可以看到這個函數就是獲得一個內部迭代器句柄然后再用NewDBIterator包裝返回一個DBIter,這個DBIter的目的就是作為內部迭代器的橋接封裝的作用,方便用戶調用。其接口函數大致有:
virtual bool Valid() const; virtual Slice key() cons; virtual Slice value() const; virtual Status status() const ; virtual void Next(); virtual void Prev(); virtual void Seek(const Slice& target); virtual void SeekToFirst(); virtual void SeekToLast();
這些封裝只是對InternalIterator的一個簡單封裝,他們都以依賴於一個這個InternalIterator。我們來看看InternalIterator的獲取
Iterator* DBImpl::NewInternalIterator(const ReadOptions& options, SequenceNumber* latest_snapshot, uint32_t* seed) { IterState* cleanup = new IterState; mutex_.Lock(); *latest_snapshot = versions_->LastSequence(); // Collect together all needed child iterators std::vector<Iterator*> list; list.push_back(mem_->NewIterator()); mem_->Ref(); if (imm_ != NULL) { list.push_back(imm_->NewIterator()); imm_->Ref(); } versions_->current()->AddIterators(options, &list); Iterator* internal_iter = NewMergingIterator(&internal_comparator_, &list[0], list.size()); versions_->current()->Ref(); cleanup->mu = &mutex_; cleanup->mem = mem_; cleanup->imm = imm_; cleanup->version = versions_->current(); internal_iter->RegisterCleanup(CleanupIteratorState, cleanup, NULL); *seed = ++seed_; mutex_.Unlock(); return internal_iter; }
這里internal_iter的獲取是從memTable、imm、還有version取得的所有與迭代器全部傳入到一個MergingIterator中。在詳細介紹這個MergingIterator前我們先來看看一個簡化的例子,並且假設此時沒有imm_table。這樣我們現在有一個Memtable,SSTable中level0有兩個文件,有一個level1的文件,里面的key大致如下(我們根據新舊程度排序,數據新舊依據請查閱Compaction章節):
Memtable: 1,2,3,4,5,6
level0-2: 3,4,7,6
level0-1: 2,3,4,6
level1: 1,3,6,7,9…
這樣,如果我們開始從第一個key開始以遞增(Next)的方式遍歷整個數據庫,那么我們可以見到如下的過程。首先是每個初始化一個指向當前文件(這里暫時將Memtable也當做一個文件)第一個位置的指針,如下紅色表示當前指向的指針:
Memtable: 1,3,4,5,6
level0-2: 3,4,7,6
level0-1: 2,3,4,6
level1: 1,3,6,7,9…
這樣,我們根據數據最新關系我們很容易判斷第一個key應該為Memtable中的1,我們記該當前key為1。然后再調用Next,調用Next的時候就需要將Memtable和level1中的當前指針key為1的向后一個key(level1中的移動在leveldb中是在FindSmallest中進行的),得到如下:
Memtable: 1,3,4,5,6
level0-2: 3,4,7,6
level0-1: 2,3,4,6
level1: 1,2,6,7,9…
那么這個時候我們也很容以判斷這里的next的值應該是level0-1中的2。那么我做出這個判斷的過程是怎么樣的呢?應該是找出當前每個文件中指針指向的值中最小的那個key,如果有多個文件中當前指針key相同的時候,那么就應該取最新的那個文件中。再繼續Next,
Memtable: 1,3,4,5,6
level0-2: 3,4,7,6
level0-1: 2,3,4,6
level1: 1,2,6,7,9…
那么此時應該是Memtable中的3。那么此時我們需要先前查找當前3的前一個呢?很明顯我們應該回到上面第三個圖的狀態,應該level1,level0-1都進行回溯,然后選擇最小的那個。但是如何能回到該狀態呢?如果這樣的話我們必須記錄每次移動的過程,這種過程性的記錄在程序設計中是十分難以做到的。而在leveldb中也采用了另外一種方式,就是在我們的迭代器器中記錄一個當前遍歷的值比如此時的level0-1中的3進行一個Prev,然后再查找最大值,最大值方式的時候如果大於3就繼續往前回溯,再找到最大的最新的。形成的狀態如下:
Memtable: 1,3,4,5,6 //找到3,然后在prev到1
level0-2: 3,4,7,6 //此處其實應該為invalid,即找到3,prev到invalid
level0-1: 2,3,4,6 //找到3,prev到2
level1: 1,2,6,7,9… /找到6,prev到2
而查找的最大最新值也應該是level0-1中的2。
下面我們來看看代碼Prev的實現:
virtual void Prev() { if (direction_ != kReverse) {// 如果之前遍歷方向向后 for (int i = 0; i < n_; i++) { IteratorWrapper* child = &children_[i]; if (child != current_) { child->Seek(key());// 查找遍歷當前值,然后再往前回溯 if (child->Valid()) { // Child is at first entry >= key(). Step back one to be < key() child->Prev(); } else { //沒有>當前key值的key. child->SeekToLast(); } } } direction_ = kReverse; } current_->Prev(); FindLargest(); }
virtual void Next() {
if (direction_ != kForward) { for (int i = 0; i < n_; i++) { IteratorWrapper* child = &children_[i]; if (child != current_) { child->Seek(key()); if (child->Valid() &&// 如果key為當前key相等,向后next comparator_->Compare(key(), child->key()) == 0) { child->Next(); } } } direction_ = kForward; } current_->Next(); FindSmallest(); }
所以再調用Next的過程就為:查找 >= 2的,如果找到並且==2就Next,然后找最小的最新的一個位置。
Memtable: 1,3,4,5,6 //找到3
level0-2: 3,4,7,6 //找到3
level0-1: 2,3,4,6 //這里是先找到2,然后再Next
level1: 1,2,6,7,9… //同上
所以這里的操作就變成了查找
我們詳細看看Next
void DBIter::Next() { assert(valid_); if (direction_ == kReverse) { // Switch directions? direction_ = kForward; // 如果上次已經到最后,回溯到第一個 if (!iter_->Valid()) { iter_->SeekToFirst(); } else { iter_->Next(); } if (!iter_->Valid()) { valid_ = false; saved_key_.clear(); return; } // saved_key_ already contains the key to skip past. } else { // 存儲當前key,以備下次為Prev時查找這個key. SaveKey(ExtractUserKey(iter_->key()), &saved_key_); } FindNextUserEntry(true, &saved_key_); }
這里由於leveldb遍歷數據庫時涉及到多個數據文件及內存中的Memtable,所以每次調用prev和next時會有比較復雜的處理。
void DBIter::FindNextUserEntry(bool skipping, std::string* skip) { // Loop until we hit an acceptable entry to yield assert(iter_->Valid()); assert(direction_ == kForward); do { ParsedInternalKey ikey; if (ParseKey(&ikey) && ikey.sequence <= sequence_) { switch (ikey.type) { case kTypeDeletion: // 如果為刪除,標記后面的已刪除的key應該跳過 // 保存跳過的key SaveKey(ikey.user_key, skip); skipping = true; break; case kTypeValue: if (skipping && user_comparator_->Compare(ikey.user_key, *skip) <= 0) { // 小於等於,跳過 } else {//找到值,返回 valid_ = true; saved_key_.clear(); return; } break; } } iter_->Next(); } while (iter_->Valid()); saved_key_.clear(); valid_ = false; }
這里我們不再對DBIter中的其他函數進行一一介紹,比如Prev和這里也是一個類似的(但是比較相反)處理過程。稍微提一下的是我們在void DBIter::FindPrevUserEntry() 中有如下一段代碼
if (saved_value_.capacity() > raw_value.size() + 1048576) { std::string empty; swap(empty, saved_value_); }