LevelDB源码:层级实现的思考

解答一个问题:

SST 落盘后是固定的,在磁盘中是没有所谓层级结构的,那么 LSM-Tree 是怎么记录每一层有哪些 SST 的呢?

首先,肯定不能仅仅靠内存来记录,因为是不持久的。

那么,接下来就会想到文件指针,LSM-Tree 的每一个 SST 会不会都是一个文件指针呢?

显然也不是,因为指针是内存中的概念,断电后就消失了,重启程序地址就会变。

那到底通过什么方式记录下SST在哪一层呢?

=> 文件编号

因为编号是整合入文件名中的,所以该索引是持久化的。

那么具体怎么操作呢?

=> VersionEdit -> Version -> Manifest

首先,在每次Compaction过后,新/旧SST的编号都会记录在VersionEdit中

之后就可以通过 builder.Apply() 和 builder.SaveTo() 将 Edit 重放入 Version 了

Version 怎么实现呢?

简单的来说,就是个数据结构记录每层有哪些SST编号,可以理解为编号数组。

那断电后怎么恢复呢?

这就是 Manifest 了,因为 Manifest 中记录的正是 VersionEdit。

所以可以通过 Recover() 重放其中的 VersionEdit 以达到恢复的目的。

总结一下:

层级结构实际就是 Version 中记录了每层有哪些文件编号

而编号是整合进文件名中的,是持久化的。

当崩溃重启后,通过重放Manifest中的VersionEdit即可恢复了。


上述思考,可以在 Flush 操作中得到验证:

// 将im memtable(以下统称im)给Flush
// 流程如下:
// 1.通过BuildTable()来生成SST
// 2.通过PickLevelForMemTableOutput()来选出存放该SST的Level
// 3.通过edit->AddFile()将新的SST加入VersionEdit中
Status DBImpl::WriteLevel0Table(MemTable* mem, VersionEdit* edit,
                                Version* base) {
  mutex_.AssertHeld();
  const uint64_t start_micros = env_->NowMicros();
  FileMetaData meta;
  meta.number = versions_->NewFileNumber();
  // 加入待生成SST列表
  pending_outputs_.insert(meta.number);
  Iterator* iter = mem->NewIterator();
  Log(options_.info_log, "Level-0 table #%llu: started",
      (unsigned long long)meta.number);

  Status s;

  // 根据im的iter生成SST
  {
    mutex_.Unlock();
    // 注意,BuildTable是包含落盘的
    // SST中的一些信息(最大最小key、文件大小)等会记录在meta中
    s = BuildTable(dbname_, env_, options_, table_cache_, iter, &meta);
    // 至此,SST已经在磁盘中了
    mutex_.Lock();
  }

  Log(options_.info_log, "Level-0 table #%llu: %lld bytes %s",
      (unsigned long long)meta.number, (unsigned long long)meta.file_size,
      s.ToString().c_str());
  // 释放iter
  delete iter;
  // 从待生成列表中删除
  pending_outputs_.erase(meta.number);

  // Note that if file_size is zero, the file has been deleted and
  // should not be added to the manifest.
  int level = 0;
  if (s.ok() && meta.file_size > 0) {
    const Slice min_user_key = meta.smallest.user_key();
    const Slice max_user_key = meta.largest.user_key();
    if (base != nullptr) {
      // 从当前Version中选出存放该SST的Level
      // 一般来说是0,当然也可能不是
      level = base->PickLevelForMemTableOutput(min_user_key, max_user_key);
    }
    // 新的SST加入new_files_中
    // 实际上是编号(meta.number)加入
    edit->AddFile(level, meta.number, meta.file_size, meta.smallest,
                  meta.largest);
  }

  CompactionStats stats;
  stats.micros = env_->NowMicros() - start_micros;
  stats.bytes_written = meta.file_size;
  stats_[level].Add(stats);
  return s;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值