Version-levelDB源码解析


【数据成员介绍】

compaction_level_:下一个需要做compact的层级;

compaction_score_:比分,如果比分小于1,就代表没有那么紧急去需要做compact;

那么比分的规则是什么呢?

void VersionSet::Finalize(Version* v) {

    int best_level = -1;

    double best_score = -1;

    for (int level = 0; levle < config::kNumberLevels - 1; level++) {

        double score;

        if (levle == 0) {

            score = v->files_[level].size() / static_cast<double>(config::kL0_CompactionTrigger);

        } else {

            const uint64_t level_bytes = TotalfileSize(v->files_[level]);

            score = static_cast<double>(level_byles) / MaxBylesForLevel(level);

        }   

        if (score > best_score) {

            best_level = level;

            best_score = score;

        }

    }

    v->compaction_levle_ = best_level;

    v->compaction_score = best_score;

}

从这个函数就可以看出,挑选出比分最高的层,就是下一个最需要做compact的层。从函数中可以看出,level 0的比分规则和其他层不一样,level 0是按照已有的sst文件数/一个限制值(这里是4),其它层是按照文件大小来比的,现有文件大小/该层文件大小限制。每层的文件大小限制versionset里面有讲,这里就不啰嗦了。为什么采用不同的标准来比分呢?有两个原因:一方面当write-buffer很大的时候,不用很频繁的对level 0做compact,另一方面,当读取的时候,文件太多会导致merge的成本变高,所以level 0的文件数要限制。


file_to_compact_;基于seek stats来决定下一个要compact的文件

file_to_compact_level_;file_to_compact_所在的层级;

那么挑选下一个进行compact的规则又是什么呢?这个放在后面说。


files_:该版本每个level的sst文件的FileMetaData

next_:指向下一个版本的指针;

prev_:指向上一个版本的指针;

refs_:引用计数;

vset_:指向一个versionset



【方法成员介绍】

Status Version::Get(const ReadOptions& options,const LookupKey& k,std::string* value,GetStats* stats)

作用:获得要查询key的值

Get方法,是在取数据的时候调用,如果能通过MemTable(Immutable Memtable、Memtable)找到,就不会调这个。下面分析下这个方法的逻辑。

step1:通过files_,循环每一层,根据leveldb的特性,如果这个key在上层能找到,就不用继续往下层找了,因为上层的数据一定比下层更新。

step2:level 0比较特殊,它的sst文件之间的key范围是存在交叠的,所以首先要找出包含key的所有sst,然后将其排序,最新(文件编号越大越新)的放前面;其他level(大于0)存在一个特性,就是sst文件之间的key  range是没有交叠的,这么设计的目的就是为了现在,在读取的时候不用处理交叠的情况,提高读的效率,所以只要找出一个包含要查找key的sst文件即可,就可以结束在该层的查找了。

step3:在sst中查找,先在table cache中找(table cache中采用的是LRU算法,后面详细介绍),找不到再读文件。



void Version::GetOverlappingInputs(int level,const InternalKey* begin,const InternalKey* end,std::vector<FileMetaData*>* inputs)

作用:获得在某一个level中和key range[begin,end]有交叠的所有文件,结果保存在inputs,如果是level 0,并且有新增的文件,会扩大这个range,重新搜索。


bool Version::OverlapInLevel(int level,const Slice* smallest_user_key,const Slice* largest_user_key)

作用:判断指定范围的key range[smallest_user_key,largest_user_key]在指定的level是否有交叠。


int Version::PickLevelForMemTableOutput(const Slice& smallest_user_key,const Slice& largest_user_key)

作用:为memTable的dump到sst挑选出一个合适的层。很多人以为数据是从memtable直接dump到level 0 的,其实不然。让我先分析下这个函数的逻辑吧。

step1:如果range[smallest_user_key,largest_user_key]和level 0有交叠,那么就选level 0,这个时候判断是否有交叠的函数(bool Version::OverlapInLevel)就派上用场了。

step2:当level < config::kMaxMemCompactLevel情况下进行循环,如果和level + 1层没有交叠,和level + 2层交叠的sst文件大小没有超过kMaxGrandParentOverlapBytes,那么就往下一层进行选择;config::kMaxMemCompactLevel=2,也就是说挑选出来的level要么是level 0层,要么是level 1层。

这么做的目的,我觉得是一个折中的策略,一方面不直接放level 0,减少level 0的文件数,就可以提高读取的效率,还可以减少compact;另一方面,kMaxMemCompactLevel=2,如果太大,在读取的时候,会增加seek的次数。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章