leveldb源码分析之五

options.create_if_missing = true;##前言
之前篇章写了数据库的初始化,貌似啥没干太多事情,其实连文件都没创建,仅仅是在内存中保持了一个wrateable的数据结构,应该是按需创建的逻辑,真的数据库创建更是距离遥远,这里我们暂时不追究,继续我们源码阅读,看看到底如何创建的。

正文

其实db_->open,做了很多事情,这里很难完全解释,我们只好在自己写个最简单的测试代码,创建一个数据库,然后运行,

leveldb::Options options;
    options.reuse_logs = false;
    options.create_if_missing = true;
    leveldb::DB* db_;
    std::string dbname_ = "/mnt/c/work/source/testdb.db";

    leveldb::DB::Open(options, dbname_, &db_);
    db_->Put(leveldb::WriteOptions(),"love","life");//
        db_->Put(leveldb::WriteOptions(),"hello","world");

第一次运行后,在特定目录中产生了这么几个文件
https://img-blog.csdn.net/20170810210713341?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvcXFfMjgyODIzMTc=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast” alt=”这里写图片描述” title=”” />olve/70/gravity/SouthEast)
第二次就运行就产生了这么几个文件
这里写图片描述
这就很奇怪,这里我们暂时不要深入研究,我们会发现到底我们如何创建这些文件,以及如何存储的,且听我们一一道来,最核心的DB::Open(options, dbname_, &db_)
传入参数就是路径,db_这个很简单,就是我们的一个指针,可以操作数据库。
那么我们就开始今天的主题,

Status DB::Open(const Options& options, const std::string& dbname,
                DB** dbptr) {
  *dbptr = NULL;

  DBImpl* impl = new DBImpl(options, dbname);  //这个才是我们最终需要的,这里初始化一大堆东西
  impl->mutex_.Lock();
  VersionEdit edit;
  // Recover handles create_if_missing, error_if_exists
  bool save_manifest = false;
  Status s = impl->Recover(&edit, &save_manifest);  //这个是最关键的步骤,我们还

这个先new一个DBImpl,这个其实就是我们客户端用的变量,我们这里不再详细介绍,其实就是初始化一些东西。最关键的impl->Recover,这里我们好好分析。

Status DBImpl::Recover(VersionEdit* edit, bool *save_manifest) {
  mutex_.AssertHeld();

  // Ignore error from CreateDir since the creation of the DB is
  // committed only when the descriptor is created, and this directory
  // may already exist from a previous failed creation attempt.
  env_->CreateDir(dbname_);
  assert(db_lock_ == NULL);
  Status s = env_->LockFile(LockFileName(dbname_), &db_lock_);  //创建一个LOCK这个文件
  if (!s.ok()) {
    return s;
  }

  if (!env_->FileExists(CurrentFileName(dbname_))) {
    if (options_.create_if_missing) {
      s = NewDB();  //这里创建了  MANIFEST-000001这个文件 ,以及CURRENT这俩文件
      if (!s.ok()) {
        return s;
      }
    } else {
      return Status::InvalidArgument(
          dbname_, "does not exist (create_if_missing is false)");
    }
  } else {
    if (options_.error_if_exists) {
      return Status::InvalidArgument(
          dbname_, "exists (error_if_exists is true)");
    }
  }

  s = versions_->Recover(save_manifest);  //这个其实是创建LOCK文件
  if (!s.ok()) {
    return s;
  }
  SequenceNumber max_sequence(0);

  // Recover from all newer log files than the ones named in the
  // descriptor (new log files may have been added by the previous
  // incarnation without registering them in the descriptor).
  //
  // Note that PrevLogNumber() is no longer used, but we pay
  // attention to it in case we are recovering a database
  // produced by an older version of leveldb.
  const uint64_t min_log = versions_->LogNumber();
  const uint64_t prev_log = versions_->PrevLogNumber();
  std::vector<std::string> filenames;
  s = env_->GetChildren(dbname_, &filenames);
  if (!s.ok()) {
    return s;
  }
  std::set<uint64_t> expected;
  versions_->AddLiveFiles(&expected);
  uint64_t number;
  FileType type;
  std::vector<uint64_t> logs;
  for (size_t i = 0; i < filenames.size(); i++) {
    if (ParseFileName(filenames[i], &number, &type)) {
      expected.erase(number);
      if (type == kLogFile && ((number >= min_log) || (number == prev_log)))
        logs.push_back(number);
    }
  }
  if (!expected.empty()) {
    char buf[50];
    snprintf(buf, sizeof(buf), "%d missing files; e.g.",
             static_cast<int>(expected.size()));
    return Status::Corruption(buf, TableFileName(dbname_, *(expected.begin())));
  }

  // Recover in the order in which the logs were generated
  std::sort(logs.begin(), logs.end());
  for (size_t i = 0; i < logs.size(); i++) {
    s = RecoverLogFile(logs[i], (i == logs.size() - 1), save_manifest, edit,
                       &max_sequence);
    if (!s.ok()) {
      return s;
    }

    // The previous incarnation may not have written any MANIFEST
    // records after allocating this log number.  So we manually
    // update the file number allocation counter in VersionSet.
    versions_->MarkFileNumberUsed(logs[i]);
  }

  if (versions_->LastSequence() < max_sequence) {
    versions_->SetLastSequence(max_sequence);
  }

  return Status::OK();
}

我擦,这也太长了吧,我们暂时只追踪第一次运行是偶逻辑,整体就是创建一个current,和mainfest文件,然后创建lock,然后恢复log中的文件。关于恢复这里的东西,其实相当复杂,这里不一一介绍,最后我们数据写入后,我们慢慢回头研究,继续Open函数

  if (s.ok() && impl->mem_ == NULL) {
    // Create new log and a corresponding memtable.
    uint64_t new_log_number = impl->versions_->NewFileNumber();
    WritableFile* lfile;
    s = options.env->NewWritableFile(LogFileName(dbname, new_log_number),
                                     &lfile);  //log文件有了,所有的文件都创建了,内容我们以后分析。
    if (s.ok()) {
      edit.SetLogNumber(new_log_number);
      impl->logfile_ = lfile;
      impl->logfile_number_ = new_log_number;
      impl->log_ = new log::Writer(lfile);
      impl->mem_ = new MemTable(impl->internal_comparator_);
      impl->mem_->Ref();
    }
  }
  if (s.ok() && save_manifest) {
    edit.SetPrevLogNumber(0);  // No older logs needed after recovery.
    edit.SetLogNumber(impl->logfile_number_);
    s = impl->versions_->LogAndApply(&edit, &impl->mutex_);
  }
  if (s.ok()) {
    impl->DeleteObsoleteFiles();
    impl->MaybeScheduleCompaction();
  }
  impl->mutex_.Unlock();
  if (s.ok()) {
    assert(impl->mem_ != NULL);
    *dbptr = impl;
  } else {
    delete impl;
  }
  return s;
}

这里吧几个文件都出初始化,到底存入了什么,我们不是很了解,这里我们不一一验证。LOCK没内容,是一个锁,LOG需要以后加内容,这里有两个复杂的CURRENT和MASINFEST比较复杂,我们直接进入看,在哪里创建,很简单,

Status DBImpl::NewDB() {
  VersionEdit new_db;
  new_db.SetComparatorName(user_comparator()->Name());
  new_db.SetLogNumber(0);
  new_db.SetNextFile(2);
  new_db.SetLastSequence(0);

  const std::string manifest = DescriptorFileName(dbname_, 1);
  WritableFile* file;
  Status s = env_->NewWritableFile(manifest, &file);
  if (!s.ok()) {
    return s;
  }
  {
    log::Writer log(file);
    std::string record;
    new_db.EncodeTo(&record);
    s = log.AddRecord(record);
    if (s.ok()) {
      s = file->Close();
    }
  }
  delete file;
  if (s.ok()) {
    // Make "CURRENT" file that points to the new manifest file.
    s = SetCurrentFile(env_, dbname_, 1);
  } else {
    env_->DeleteFile(manifest);
  }
  return s;
}

这里其实是创建一个new_db,然后初始化,最终把版本信息保存到MAINFEST中,然后让通过SetCurrentFile(env_, dbname_, 1);,注意这里文件后缀都是1,和文中的差异,这里暂时没追踪到,以后解释。

后记

这篇博客写的一波三折,还有很多矛盾,这里不一一解释,我们只要追踪到我们主要流程,大概记录下,以后有机会完善下

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值