leveldb:DBIter
来源:互联网 发布:项目管理就业前景知乎 编辑:程序博客网 时间:2024/06/09 23:11
DBIter的作用
Leveldb数据库的MemTable和sstable文件的存储格式都是InternalKey(userkey, seq, type) => uservalue。
DBIter把同一个userkey在DB中的多条记录合并为一条,综合考虑了userkey的序号、删除标记、和写覆盖等等因素。DBIter只会把userkey最新(seq最大的就是最新的,相同userkey的老记录(seq较小的)不会让上层看到)的一条记录展现给用户,另外如果这条最新的记录是删除类型,则会跳过该记录,否则,遍历时会把已删除的key列举出来。
DBIter的实现原理
class DBIter: public Iterator { enum Direction { kForward, kReverse };private: DBImpl* db_; const Comparator* const user_comparator_;//比较iter间userkey Iterator* const iter_;//是一个MergingIterator SequenceNumber const sequence_;//DBIter只能访问到比sequence_小的kv对 //这就方便了老版本(快照)数据库的遍历 Status status_; std::string saved_key_; //用于方向遍历 direction_==kReverse时才有效 std::string saved_value_; //用于反向遍历 direction_==kReverse时才有效 Direction direction_; bool valid_; Random rnd_; ssize_t bytes_counter_;}
iter_是DBImpl::NewInternalIterator创建的结果
std::vector<Iterator*> list; list.push_back(mem_->NewIterator());//加入memtbale的迭代器 list.push_back(imm_->NewIterator());//加入immemtbale的迭代器 versions_->current()->AddIterators(options, &list);//可以简单理解为加入所以sst文件的迭代器 Iterator* internal_iter = NewMergingIterator(&internal_comparator_, &list[0], list.size());
iter_是由NewInternalIterator创建的一个MergingIterator,通过MergingIterator可以实现多个有序数据集合的归并操作。其中包含多个child iterator组成的集合。对MergingIterator的遍历会有序的遍历其child iterator中的每个元素。
因为iter_遍历的是数据库的每一条记录。它是以InternalKey(userkey, seq, type)为遍历粒度的,只要InternalKey中任意一个组成元素不同,MergingIterator就认为他们是不同的kv对。
而DBIter是以userkey为遍历粒度的,只要记录的userkey相同,那么DBIter就认为他们是一条记录(不同版本),sqe越大代表该记录越新。每次迭代将跳到下一个不同userkey的记录。
假设当前DBIter的sequence_为2,那么DBIter只会处理seq小于2的记录,这也是访问快照数据的原理。
图中每个框框表示对应记录的InternalKey(userkey, seq, type),key1 < key2 < key3 < key4,seq越大代表记录越新,所以相同userkey的seq越大的顺序越靠前(小)。
DBIter next的实现
假设iter_现在在key2:2:1的位置,DBIter也在这个位置,此时iter_的next会是key2:1:1。而DBIter的next将会是key4:2:1(因为key3:3:1seq不符,key3:2:0是一条删除记录,因此跳过key3:2:0和key3:1:1,key4:2:1seq不符)。
DBIter prev的实现
假设当前iter在key4:2:1上,direction为kForward。此时调用Prev(),此图显示了Prev操作执行的5个步骤。
S1 首先因为direction为kForward,先调整iter到key3:1:1上。此图也说明了调整的理由,key4:2:1前面还有key4:3:1。然后进入FindPrevUserEntry函数,执行S2到S4。
S2 跳到key3:2:0上时,这是一个删除标记,清空saved key(其中保存的是key3:1:1)。
S3 循环继续,跳到key2:1:1上,此时key2:1:1 > saved key,设置saved key为key2:1:1,并继续循环。
S4 循环继续,跳到key2:2:1上,此时key2:2:1 > saved key,设置saved key为key2:2:1,并继续循环。
S5 跳到Key1:1:1上,因为key1:1:1 < saved key,跳出循环。
最终状态iter_位置在key1:1:1上,而saved key保存的则是key2:2:1上,这也就是Prev应该定位到的值。也就是说在Prev操作下,iter_的位置并不是真正的key位置。这就是Get函数中,在direction为kReverse时,返回saved key/value的原因。
同理,在Next时,如果direction是kReverse,根据上面的Prev可以发现,此时iter刚好是saved key的前一个entry。执行iter->Next()就跳到了saved key的dentry范围的sequence最大的那个entry。在前面的例子中,在Prev后执行Next,那么iter首先跳转到key2:3:1上,然后再调用FindNextUserEntry循环,使iter定位在key2:2:1上。
prev的小结
prev就是要向前找到第一个符合条件的kv记录,该记录的userkey必须小于当前iter的userkey,并且该记录是最新的而且不能是删除类型的记录。如何判断出该记录是不是最新的呢,先将该记录保存在save(saved key/value)里,iter_继续向前遍历,如果遇到userkey和刚才save的记录userkey不相等的,则说明刚才save的就是最新的记录,此时iter_的位置并不是真正的key位置,saved key才是真正key的位置,因此需要用Direction来区分。
Direction
1) kForward,正向遍历(向后移动),代码保证此时DBIter的内部迭代器刚好定位在this->key(),this->value()这条记录上;此时iter_一定是在一条最新的记录上(seq最大的记录上(当然seq得小于sequence_))
2) kReverse,反向遍历(向前移动),代码保证此时DBIter的内部迭代器刚好定位在所有key=this->key()的entry之前。此时iter_一定是在一条最老的记录上。
其成员变量savedkey和saved value保存的是KReverse方向移动完成后的k/v对,每次seek系调用之后,其值都会跟随iter_而改变。
代码分析
FindNextUserEntry和FindPrevUserEntry
FindNextUserEntry和FindPrevUserEntry的功能就是循环跳过下一个/前一个delete的记录,直到遇到kValueType的记录。
void DBIter::FindNextUserEntry(bool skipping, std::string* skip)
参数@skipping表明是否要跳过userkey和skip相等的记录;
参数@skip临时存储空间,保存seek时要跳过的key;
在进入FindNextUserEntry时,iter_刚好定位在this->key(), this->value()这条记录上。
//skipping等于true的时候表示要找到向后找到第一个和skip不相等的userkey才行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_) {//跳过不符合sequence_要求的kv对 switch (ikey.type) { case kTypeDeletion://如果遇到iter userkey的删除操作,则说明后面该userkey都是无效的,因此需要跳过 // Arrange to skip all upcoming entries for this key since // they are hidden by this deletion. SaveKey(ikey.user_key, skip); skipping = true; break; case kTypeValue: if (skipping && user_comparator_->Compare(ikey.user_key, *skip) <= 0) {//如果当前key与要跳过的key相等就继续向后 // Entry hidden } else { valid_ = true;//如果skipping为true的话,此处iter为第一个不等于save的userkey,而且不是删除类型 saved_key_.clear(); return; } break; } } iter_->Next(); } while (iter_->Valid()); saved_key_.clear(); valid_ = false;}
FindNextUserKey移动方向是kForward,DBIter在向kForward移动时,借用了saved key作为临时缓存。FindNextUserKey确保定位到的entry的sequence不会大于指定的sequence,并跳过被删除标记覆盖的旧记录。
void DBIter::FindPrevUserEntry(),在进入FindPrevUserEntry时,iter_刚好位于saved key对应的所有记录之前*FindPrevUserKey根据指定的sequence,依次检查前一个entry,直到遇到user key小于saved key,并且类型不是Delete的entry。*
如果entry的类型是Delete,就清空saved key和saved value,这样在依次遍历前一个entry的循环中,只要类型不是Delete,就是要找的entry。这就是Prev的语义。
void DBIter::FindPrevUserEntry() { assert(direction_ == kReverse); ValueType value_type = kTypeDeletion;//这个很关键,后面的循环至少执行一次Prev操作 if (iter_->Valid()) { do { ParsedInternalKey ikey; if (ParseKey(&ikey) && ikey.sequence <= sequence_) { if ((value_type != kTypeDeletion) &&//第一次是不会进入该分支的,因为进入循环前先把value_type = kTypeDeletion; user_comparator_->Compare(ikey.user_key, saved_key_) < 0) { // We encountered a non-deleted value in entries for previous keys, break; } //根据类型,如果是Deletion则清空saved key和saved value //否则,把iter_的user key和value赋给saved key和saved value value_type = ikey.type; if (value_type == kTypeDeletion) { saved_key_.clear(); ClearSavedValue(); } else { Slice raw_value = iter_->value(); if (saved_value_.capacity() > raw_value.size() + 1048576) { std::string empty; swap(empty, saved_value_); }//save一直在变,直到遇到iter和save的userkey不一样,说明save是最新的 SaveKey(ExtractUserKey(iter_->key()), &saved_key_); saved_value_.assign(raw_value.data(), raw_value.size()); } } iter_->Prev(); } while (iter_->Valid()); } if (value_type == kTypeDeletion) { // End valid_ = false; saved_key_.clear(); ClearSavedValue(); direction_ = kForward; } else { valid_ = true; }}
seek
void DBIter::Seek(const Slice& target) { direction_ = kForward; // 向前seek // 清空saved value和saved key,并根据target设置saved key ClearSavedValue(); saved_key_.clear(); AppendInternalKey( // kValueTypeForSeek(1) > kDeleteType(0) &saved_key_,ParsedInternalKey(target, sequence_, kValueTypeForSeek)); iter_->Seek(saved_key_); // iter seek到saved key //可以定位到合法的iter,还需要跳过Delete的entry if (iter_->Valid()) FindNextUserEntry(false,&saved_key_); //是false else valid_ = false; } void DBIter::SeekToFirst() { direction_ = kForward; // 向前seek // 清空saved value,首先iter_->SeekToFirst,然后跳过Delete的entry ClearSavedValue(); iter_->SeekToFirst(); if (iter_->Valid()) FindNextUserEntry(false,&saved_key_ /*临时存储*/); else valid_ = false; } void DBIter::SeekToLast() { // 更简单 direction_ = kReverse; ClearSavedValue(); iter_->SeekToLast(); FindPrevUserEntry(); }
参考:http://blog.csdn.net/sparkliang/article/details/16968487
- leveldb:DBIter
- LevelDb
- LevelDb
- LevelDb
- leveldb
- leveldb
- leveldb
- leveldb
- LevelDb
- LevelDb
- Leveldb
- Leveldb
- levelDB
- LevelDb
- leveldb
- LevelDb
- LevelDb
- LevelDb
- Haskell语言学习笔记(45)Profunctor
- NBUT 1619
- C语言指针详述
- Haskell语言学习笔记(46)Parsec(3)
- NOIP2017 列队 线段树(指针版)+vector
- leveldb:DBIter
- sQL学习(2)
- Android动态设置控件等宽高
- Haskell语言学习笔记(47)Arrow(2)
- UNITY开发中常用的一些API
- 使用MySQL设计大学学籍管理数据库(后台数据库+前台程序)
- Unity C#执行bat脚本
- Haskell语言学习笔记(48)Data.Tuple
- HTML+CSS的练习之网上书城主页面的实现