[LevelDB] LevelDB之打开和创建

LevelDB文件类型

下面的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; }

---------------------
作者:uestc-leon
来源:CSDN
原文:https://blog.csdn.net/weixin_36145588/article/details/78487006
版权声明:本文为博主原创文章,转载请附上博文链接! 

猜你喜欢

转载自www.cnblogs.com/mh1092/p/9951342.html