天天看點

leveldb源碼解析四——版本管理

leveldb支援資料的多個版本,可以擷取某個版本的快照,周遊或者查找這個版本中的key

class LEVELDB_EXPORT DB {
  ...
  // Return a handle to the current DB state.  Iterators created with
  // this handle will all observe a stable snapshot of the current DB
  // state.  The caller must call ReleaseSnapshot(result) when the
  // snapshot is no longer needed.
  virtual const Snapshot* GetSnapshot() = 0;

  // Release a previously acquired snapshot.  The caller must not
  // use "snapshot" after this call.
  virtual void ReleaseSnapshot(const Snapshot* snapshot) = 0;
  ...
};
           

同時對某個key更新時,不會直接在原key上更新,而是送出一個版本号更大的record,原版本的key會一直保留,直到在compaction時,低版本的key被merge到高版本的key。

Version

Version儲存的是目前版本中的sstable和相關compaction資訊。通過引用計數管理生命周期,通過list方式組織。Version封裝了在compaction時對sstable的一系列操作。

class Version {
  ...
  VersionSet* vset_;  // VersionSet to which this Version belongs
  Version* next_;     // Next version in linked list
  Version* prev_;     // Previous version in linked list
  int refs_;          // Number of live refs to this version

  // List of files per level
  std::vector<FileMetaData*> files_[config::kNumLevels];

  // Next file to compact based on seek stats.
  FileMetaData* file_to_compact_;
  int file_to_compact_level_;

  // Level that should be compacted next and its compaction score.
  // Score < 1 means compaction is not strictly needed.  These fields
  // are initialized by Finalize().
  double compaction_score_;
  int compaction_level_;
};
           

VersionEdit

每次compaction之後,會産生一個新的版本,新版本在sstable檔案組成上面和老版本上存在差異,這個差異通過VersionEdit表示,老版本通過應用VersionEdit之後,就可以得到新版本。

struct VersionEdit {

  typedef std::set<std::pair<int, uint64_t>> DeletedFileSet;
  // 比較器名稱
  std::string comparator_;
  // 日志編号,該日志之前的資料均可删除
  uint64_t log_number_;
  // 已經棄用
  uint64_t prev_log_number_;
  // 下一個檔案編号(ldb、idb、MAINFEST檔案共享一個序号空間)
  uint64_t next_file_number_;
  // 最後的seq_num
  SequenceNumber last_sequence_;
  // 
  bool has_comparator_;
  bool has_log_number_;
  bool has_prev_log_number_;
  bool has_next_file_number_;
  // 
  bool has_last_sequence_;
  // 記錄每一層所對應需要壓縮的
  std::vector<std::pair<int, InternalKey>> compact_pointers_;
  // 相比上次version而言,本次需要删除的檔案有哪些
  DeletedFileSet deleted_files_;
  // 相比上次version而言,本次新增的檔案有哪些
  std::vector<std::pair<int, FileMetaData>> new_files_;
};
           

VersionEdit中封裝了對上述幾個字段的序列化和反序列化。

Manifest

Manifest檔案可以看作是VersionEdit的日志,為了快速恢複需要将這些變更持久化到磁盤上。Manifest和WAL使用同樣的格式就VersionEdit,一個VersionEdit就是一條record。在DB重新開機時,通過解析目前Manifest檔案,就可以恢複到最新的版本。

VersionSet

通過VersionSet來管理多個Version,整個db隻有一個。

leveldb源碼解析四——版本管理
class VersionSet {
  ...
  Env* const env_;
  const std::string dbname_;
  const Options* const options_;
  TableCache* const table_cache_;
  const InternalKeyComparator icmp_;
  uint64_t next_file_number_;
  uint64_t manifest_file_number_;
  uint64_t last_sequence_;
  uint64_t log_number_;
  uint64_t prev_log_number_;  // 0 or backing store for memtable being compacted

  // Opened lazily
  WritableFile* descriptor_file_;
  log::Writer* descriptor_log_;
  Version dummy_versions_;  // Head of circular doubly-linked list of versions.
  Version* current_;        // == dummy_versions_.prev_

  // Per-level key at which the next compaction at that level should start.
  // Either an empty string, or a valid InternalKey.
  std::string compact_pointer_[config::kNumLevels];
  ...
};
           

繼續閱讀