【數據成員介紹】
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的次數。