leveldb:DB::Open创建一个新数据库或者打开一个已存在的数据库

leveldb文件类型

leveldb:DB::Open创建一个新数据库或者打开一个已存在的数据库_第1张图片
上面的log文件,sst文件,临时文件,清单文件末尾都带着序列号,序号是单调递增的(随着next_file_number从1开始递增),以保证不会和之前的文件名重复。另外,注意区分db log与info log:前者是为了防止保障数据安全而实现的二进制Log,后者是打印引擎中间运行状态及警告等信息的文本log
随着更新与Compaction的进行,LevelDB会不断生成新文件,有时还会删除老文件,所以需要一个文件来记录文件列表,这个列表就是清单文件(manifest文件)的作用,清单会不断变化,DB需要知道最新的清单文件,必须将清单准备好后原子切换,这就是CURRENT文件的作用,LevelDB的清单过程更新如下:
1. 递增清单序号,生成一个新的清单文件。
2. 将此清单文件的名称写入到一个临时文件中。
3. 将临时文件rename为CURRENT。

DB::Open

LevelDB无论是创建数据库,还是打开现有的数据库,都是使用Open方法。代码如下:

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;  
  bool save_manifest = false;
  // Recover逻辑,如果存在数据库,则Load数据库数据,并对日志进行恢复,否则,创建新的数据库  
  Status s = impl->Recover(&edit, &save_manifest);  //具体可看recover那篇博文
  if (s.ok() && impl->mem_ == NULL) {
  //impl->mem_ == NULL说明没有继续使用旧日志需创建新的日志
    // 从version_set获取一个新的文件序号用于日志文件,所以如果是新建的数据库,则第一个LOG的
    //序号为2(1已经被MANIFEST占用,NewDB代码里可以看出)  
    uint64_t new_log_number = impl->versions_->NewFileNumber();  
    // 记录日志文件号,创建新的log文件及Writer对象  
    WritableFile* lfile;  
    s = options.env->NewWritableFile(LogFileName(dbname, new_log_number),  
                                     &lfile);  
    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) {  
    //save_manifest说明有新的sst文件生成,需要记录在manifest文件
        edit.SetPrevLogNumber(0);  // No older logs needed after recovery.
    edit.SetLogNumber(impl->logfile_number_);
    //产生了新log,就log已经回放完毕了,应用edit生成新版本
    s = impl->versions_->LogAndApply(&edit, &impl->mutex_);
  }
    if(s.ok())
    {
      // 删除废弃的文件(如果存在)  
      impl->DeleteObsoleteFiles();  
      // 检查是否需要Compaction,如果需要,则让后台启动Compaction线程  
      impl->MaybeScheduleCompaction();  
    }  
  impl->mutex_.Unlock();  
  if (s.ok()) {  
    *dbptr = impl;  
  } else {  
    delete impl;  
  }  
  return s;  
}  

创建一个新数据库

创建一个新数据库也需要DB的Open方法
创建一个新数据库的调用流程如下:
DB::Open
DBImpl* impl = new DBImpl
impl->Recover //详见leveldb:数据库recover机制
DBImpl::NewDB

//DBImpl在构造时会初始化互斥体与信号量,创建一个空的memtable,并根据配置设置
//Comparator及LRU缓冲
DBImpl::DBImpl(const Options& raw_options, const std::string& dbname)
    : env_(raw_options.env),
      internal_comparator_(raw_options.comparator),// 初始化Comparator 
      internal_filter_policy_(raw_options.filter_policy),
      // 检查参数是否合法 
      options_(SanitizeOptions(dbname, &internal_comparator_,
                               &internal_filter_policy_, raw_options)),
      // 是拥有自己info log,还是使用用户提供的
      owns_info_log_(options_.info_log != raw_options.info_log),
      // 是否拥有自己的block LRU cache,或者使用用户提供的
      owns_cache_(options_.block_cache != raw_options.block_cache),
      dbname_(dbname),// 数据库名称
      db_lock_(NULL),
      shutting_down_(NULL),
      bg_cv_(&mutex_),// 用于与后台线程交互的条件信号
      mem_(NULL),// 跳表初识为NULL
      imm_(NULL),
      logfile_(NULL),
      logfile_number_(0),// log文件的序号
      log_(NULL),
      seed_(0),
      tmp_batch_(new WriteBatch),//用于write
      bg_compaction_scheduled_(false),// 当前是否有后台的compaction线程正在进行合并
      manual_compaction_(NULL) {
  has_imm_.Release_Store(NULL);

  // 设置table LRU cache的Entry数目不能超过max_open_files-10
  const int table_cache_size = options_.max_open_files - kNumNonTableCacheFiles;
  //table_cache_是各个sst文件元数据(index块以及布隆块)的缓存
  table_cache_ = new TableCache(dbname_, &options_, table_cache_size);
  //leveldb一共使用了两种lru cache,它们的功能不同,一个是table_cache,一个是block_cache
// 创建一个Version管理器
  versions_ = new VersionSet(dbname_, &options_, table_cache_,
                             &internal_comparator_);
}


Options SanitizeOptions(const std::string& dbname,  
                        const InternalKeyComparator* icmp,  
                        const Options& src) {  
  Options result = src;  
  result.comparator = icmp;  
  ClipToRange(&result.max_open_files,           20,     50000);  
  ClipToRange(&result.write_buffer_size,        64<<10, 1<<30);  
  ClipToRange(&result.block_size,               1<<10,  4<<20);  
  // 如果用户未指定info log文件(用于打印状态等文本信息的日志文件),则由引擎自己创建一个info log文件。  
  if (result.info_log == NULL) {  
    // Open a log file in the same directory as the db  
    src.env->CreateDir(dbname);  // 如果目录不存在则创建  
    // 如果已存在以前的info log文件,则将其改名为LOG.old,然后创建新的log文件与日志的writer  
    src.env->RenameFile(InfoLogFileName(dbname), OldInfoLogFileName(dbname));  
    Status s = src.env->NewLogger(InfoLogFileName(dbname), &result.info_log);  
    if (!s.ok()) {  
      result.info_log = NULL;  
    }  
  }  
  // 如果用户没指定block_cache LRU缓冲,则创建8MB的LRU缓冲  
  if (result.block_cache == NULL) {  
    result.block_cache = NewLRUCache(8 << 20);  
  }  
  return result;  
}  

Status DBImpl::NewDB() {  
  // 创建version管理器  
  VersionEdit new_db;  
  // 设置Comparator  
  new_db.SetComparatorName(user_comparator()->Name());  
  new_db.SetLogNumber(0);  
  // 下一个序号从2开始,1留给清单文件  
  new_db.SetNextFile(2);  
  new_db.SetLastSequence(0);  
  // 创建一个清单文件,MANIFEST-1  
  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()) {  
    // 设置CURRENT文件,使其指向清单文件  
    s = SetCurrentFile(env_, dbname_, 1);  
  } else {  
    env_->DeleteFile(manifest);  
  }  
  return s;  
}

你可能感兴趣的:(造轮子之leveldb)