leveldb原始碼分析——leveldb層次結構
這篇文章介紹了單個sstable的物理結構以及從 如何高效的從sstable中查詢key-value這個思路理解sstable設計思想。那麼當sstable檔案數量一旦多了,又如何高效查詢呢?總不可能遍歷所有sstable檔案吧,所以leveldb 又設計一個層次結構對大量的sstable檔案進行管理,便於提高key-value查詢效率。所以本文重點就是分析leveldb的層次結構以及在大量sstable檔案中高效查詢的原理。理清了這點我相信對整個leveldb的設計核心思想就基本把握了,在這個基礎上再去看 sstable的壓縮,以及version設計原始碼就十分容易了,因為這些資料結構 類的設計基本都是圍繞這個進行的。
首先上張leveldb中sstable檔案邏輯上的層次結構圖,腦袋裡應該先有這個印象。
leveldb將所有sstable在邏輯上分層次,自底向上依次是 0,1,2,…..預設最多7層。圖中每一個大方塊代表一個sstable檔案,中間兩個元素代表sstable中最大key和最小key。 files_[][] 二維陣列記錄所有sstable, files_[index]記錄level-index層所有sstable檔案。為了表述方便,用level-index[smallest,largest]形式表述每個sstable檔案,如level-0[10,100]代表level-0層最左邊sstable檔案。
當memtable 超過閾值時就會壓縮生成sstable檔案,而level-0 sstable檔案都是memtable壓縮而成的,但是由於使用者插入的key永遠都是隨機的,故而可能先後由memtable壓縮成的sstable檔案中key有重疊。在插入files_[0]的時候根據 level-0層sstable檔案中最小key 按升序插入,因此level-0層檔案files_[0]是按照level-0[10,100] 、level-0[20,80]、level-0[70,150]插入的,這點對於files_[index] (index>0)插入也是一樣的,因為這樣便於二分查詢。對level-0層就是這些特點。當level-0檔案超過一定數量的時候,就會選擇level-0 一些sstable進行壓縮 產生新的sstable,比如可以剔除一些被使用者刪除的元素,還有level-0[10,100] 、level-0[20,80] 互相重疊的元素,減少磁碟佔用量,提高查詢效率,至於壓縮過程這裡不討論,今天論述主題是leveldb各層結構。
對於level-index(index>0)層因為是壓縮而成的,所以level-index(index>0)層不同sstable檔案之間 key不會有重疊,因此滿足files_[index][i] sstable檔案最大key 一定是不大於files_[index][i+1]檔案最小key,但是不同層次間的key是有可能重疊的。關於level 不同層次間sstable 特點就是這些。
再以查詢target_key=30 進一步說明,因為level-0層檔案相對level-index(index>0)檔案總是最新的,而level-0 層序號大的sstable檔案比小的新,所以總是先從level-0 按檔案序號降序依次查詢,假如level-0檔案序號 level-0[70,150]>level-0[20,80]>level-0[10,100],那麼首先查詢 level-0[70,150],直接比較target_key是否在level-0[70,150]最大和最小值區間,不再就直接跳過,接下來比較level-0[20,80],發現在,就按照sstable解析的過程進一步查詢驗證。
再以target_key=500為例,還是先從level-0查起,沒有結果。就再查level-1,因為level-index(index>0)不同sstable檔案key沒有重疊,所以直接以二分查詢,依次從底層向高層查詢,最終level-2[201,600] 滿足target_key在最大和最小值區間。
leveldb這種層次結構的設計思想就是高效在大量sstable中找到目標sstable。
表示
對於leveldb這種層次結構,那麼對應的資料結構如何表示的呢?
這個就涉及到了 VersionSet,Version,VersionEdit這三個類了,下面結合leveldb層次結構分析這三個類的作用。
1 Version
version用於記錄所有sstable檔案,version 的定義中有幾個關鍵資料成員
class Version {
private:
VersionSet* vset_; // VersionSet to which this Version belongs //每個version持有versionSet
Version* next_; // Next version in linked list
Version* prev_; // Previous version in linked list
// List of files per level
std::vector<FileMetaData*> files_[config::kNumLevels];//記錄每層的檔案
};
std::vector < FileMetaData*> files_[config::kNumLevels] 就是上面的files_結構,記錄所有sstable檔案。FileMetaData記錄了檔案的元資訊,比如最大key,最小key,這裡的key是internal_key。所以查詢key-value的時候就可以根據version這個結構查詢到相應的sstable檔案。
2 VersionEdit
VersionEdit 就是用於記錄 memtable壓縮成sstable 或者 sstable被壓縮 產生新的sstable檔案時 產生了哪些新的sstable,以及哪些sstable不需要了。
簡單看看幾個關鍵資料成員
class VersionEdit {
typedef std::set< std::pair<int, uint64_t> > DeletedFileSet;
DeletedFileSet deleted_files_;
std::vector< std::pair<int, FileMetaData> > new_files_;
};
new_files_ 就是記錄新產生了哪些sstable檔案。
deleted_files_ 記錄需要刪除的sstable檔案,這個在sstable被壓縮 產生新的sstable檔案時才用到。
如果把上面version 理解為某一時刻sstable檔案層次結構,記錄為version1,那麼VersionEdit就記錄下一時刻變動的sstable檔案,那麼經過變動的後的version 記錄為version2,那麼可理解為version2=version1+VersionEdit
3 VersionSet
從類名上就可以理解為version的集合,記錄了所有version,每次壓縮都會形成一個version。