Open

LevelDB的初始化主要由Open函数实现:

  1. Status DB::Open(const Options &options, const std::string &dbname,
  2. DB **dbptr)

首先,Open函数调用Recover函数将LevelDB的历史状态恢复:

  1. *dbptr = nullptr;
  2. DBImpl *impl = new DBImpl(options, dbname);
  3. impl->mutex_.Lock();
  4. VersionEdit edit;
  5. // Recover handles create_if_missing, error_if_exists
  6. bool save_manifest = false;
  7. Status s = impl->Recover(&edit, &save_manifest);

如果恢复后LevelDB当前的memtable为空则创建一个memtable和相应的log文件:

  1. if (s.ok() && impl->mem_ == nullptr)
  2. {
  3. // Create new log and a corresponding memtable.
  4. uint64_t new_log_number = impl->versions_->NewFileNumber();
  5. WritableFile *lfile;
  6. s = options.env->NewWritableFile(LogFileName(dbname, new_log_number),
  7. &lfile);
  8. if (s.ok())
  9. {
  10. edit.SetLogNumber(new_log_number);
  11. impl->logfile_ = lfile;
  12. impl->logfile_number_ = new_log_number;
  13. impl->log_ = new log::Writer(lfile);
  14. impl->mem_ = new MemTable(impl->internal_comparator_);
  15. impl->mem_->Ref();
  16. }
  17. }

如果需要将新建的manifest文件保存下来,就调用LogAndApply函数将当前的VersionEdit对象应用,并在

  1. if (s.ok() && save_manifest)
  2. {
  3. edit.SetPrevLogNumber(0); // No older logs needed after recovery.
  4. edit.SetLogNumber(impl->logfile_number_);
  5. s = impl->versions_->LogAndApply(&edit, &impl->mutex_);
  6. }

最后删除旧文件并尝试进行compaction:

  1. if (s.ok())
  2. {
  3. impl->DeleteObsoleteFiles();
  4. impl->MaybeScheduleCompaction();
  5. }
  6. impl->mutex_.Unlock();
  7. if (s.ok())
  8. {
  9. assert(impl->mem_ != nullptr);
  10. *dbptr = impl;
  11. }
  12. else
  13. {
  14. delete impl;
  15. }
  16. return s;

Recover函数用于恢复LevelDB,包括恢复VersionSet和memtable:

  1. Status DBImpl::Recover(VersionEdit *edit, bool *save_manifest)

创建存储LevelDB数据的目录(如果出现错误则会被忽略,可能已经存在):

  1. mutex_.AssertHeld();
  2. // Ignore error from CreateDir since the creation of the DB is
  3. // committed only when the descriptor is created, and this directory
  4. // may already exist from a previous failed creation attempt.
  5. env_->CreateDir(dbname_);
  6. assert(db_lock_ == nullptr);
  7. Status s = env_->LockFile(LockFileName(dbname_), &db_lock_);
  8. if (!s.ok())
  9. {
  10. return s;
  11. }

如果CURRENT文件不存在(当前LevelDB为新创建时),调用NewDB函数创建LevelDB所需的相关文件(manifest文件和CURRENT文件):

  1. if (!env_->FileExists(CurrentFileName(dbname_)))
  2. {
  3. if (options_.create_if_missing)
  4. {
  5. s = NewDB();
  6. if (!s.ok())
  7. {
  8. return s;
  9. }
  10. }
  11. else
  12. {
  13. return Status::InvalidArgument(
  14. dbname_, "does not exist (create_if_missing is false)");
  15. }
  16. }
  17. else
  18. {
  19. if (options_.error_if_exists)
  20. {
  21. return Status::InvalidArgument(
  22. dbname_, "exists (error_if_exists is true)");
  23. }
  24. }

调用versions_->Recover函数根据manifest文件恢复VersionSet:

  1. s = versions_->Recover(save_manifest);
  2. if (!s.ok())
  3. {
  4. return s;
  5. }
  6. SequenceNumber max_sequence(0);

获取之前尚未处理的log文件:

  1. // Recover from all newer log files than the ones named in the
  2. // descriptor (new log files may have been added by the previous
  3. // incarnation without registering them in the descriptor).
  4. //
  5. // Note that PrevLogNumber() is no longer used, but we pay
  6. // attention to it in case we are recovering a database
  7. // produced by an older version of leveldb.
  8. const uint64_t min_log = versions_->LogNumber();
  9. const uint64_t prev_log = versions_->PrevLogNumber();
  10. std::vector<std::string> filenames;
  11. s = env_->GetChildren(dbname_, &filenames);
  12. if (!s.ok())
  13. {
  14. return s;
  15. }
  16. std::set<uint64_t> expected;
  17. versions_->AddLiveFiles(&expected);
  18. uint64_t number;
  19. FileType type;
  20. std::vector<uint64_t> logs;
  21. for (size_t i = 0; i < filenames.size(); i++)
  22. {
  23. if (ParseFileName(filenames[i], &number, &type))
  24. {
  25. expected.erase(number);
  26. if (type == kLogFile && ((number >= min_log) || (number == prev_log)))
  27. logs.push_back(number);
  28. }
  29. }
  30. if (!expected.empty())
  31. {
  32. char buf[50];
  33. snprintf(buf, sizeof(buf), "%d missing files; e.g.",
  34. static_cast<int>(expected.size()));
  35. return Status::Corruption(buf, TableFileName(dbname_, *(expected.begin())));
  36. }

通过调用RecoverLogFile函数,按顺序根据这些log文件恢复LevelDB的memtable:

  1. // Recover in the order in which the logs were generated
  2. std::sort(logs.begin(), logs.end());
  3. for (size_t i = 0; i < logs.size(); i++)
  4. {
  5. s = RecoverLogFile(logs[i], (i == logs.size() - 1), save_manifest, edit,
  6. &max_sequence);
  7. if (!s.ok())
  8. {
  9. return s;
  10. }
  11. // The previous incarnation may not have written any MANIFEST
  12. // records after allocating this log number. So we manually
  13. // update the file number allocation counter in VersionSet.
  14. versions_->MarkFileNumberUsed(logs[i]);
  15. }
  16. if (versions_->LastSequence() < max_sequence)
  17. {
  18. versions_->SetLastSequence(max_sequence);
  19. }
  20. return Status::OK();

RecoverLogFile函数用于根据log文件恢复memtable:

  1. Status DBImpl::RecoverLogFile(uint64_t log_number, bool last_log,
  2. bool *save_manifest, VersionEdit *edit,
  3. SequenceNumber *max_sequence)

首先打开log文件:

  1. // Open the log file
  2. std::string fname = LogFileName(dbname_, log_number);
  3. SequentialFile *file;
  4. Status status = env_->NewSequentialFile(fname, &file);
  5. if (!status.ok())
  6. {
  7. MaybeIgnoreError(&status);
  8. return status;
  9. }

接下来创建一个LogReporter对象用于读取log文件:

  1. // Create the log reader.
  2. LogReporter reporter;
  3. reporter.env = env_;
  4. reporter.info_log = options_.info_log;
  5. reporter.fname = fname.c_str();
  6. reporter.status = (options_.paranoid_checks ? &status : nullptr);
  7. // We intentionally make log::Reader do checksumming even if
  8. // paranoid_checks==false so that corruptions cause entire commits
  9. // to be skipped instead of propagating bad information (like overly
  10. // large sequence numbers).
  11. log::Reader reader(file, &reporter, true /*checksum*/,
  12. 0 /*initial_offset*/);
  13. Log(options_.info_log, "Recovering log #%llu",
  14. (unsigned long long)log_number);

然后读取log文件中的日志记录,将KV值插入memtable中,如果memtable的大小超过设定的阈值,就将其compact到level0:

  1. // Read all the records and add to a memtable
  2. std::string scratch;
  3. Slice record;
  4. WriteBatch batch;
  5. int compactions = 0;
  6. MemTable *mem = nullptr;
  7. while (reader.ReadRecord(&record, &scratch) &&
  8. status.ok())
  9. {
  10. if (record.size() < 12)
  11. {
  12. reporter.Corruption(
  13. record.size(), Status::Corruption("log record too small"));
  14. continue;
  15. }
  16. WriteBatchInternal::SetContents(&batch, record);
  17. if (mem == nullptr)
  18. {
  19. mem = new MemTable(internal_comparator_);
  20. mem->Ref();
  21. }
  22. status = WriteBatchInternal::InsertInto(&batch, mem);
  23. MaybeIgnoreError(&status);
  24. if (!status.ok())
  25. {
  26. break;
  27. }
  28. const SequenceNumber last_seq =
  29. WriteBatchInternal::Sequence(&batch) +
  30. WriteBatchInternal::Count(&batch) - 1;
  31. if (last_seq > *max_sequence)
  32. {
  33. *max_sequence = last_seq;
  34. }
  35. if (mem->ApproximateMemoryUsage() > options_.write_buffer_size)
  36. {
  37. compactions++;
  38. *save_manifest = true;
  39. status = WriteLevel0Table(mem, edit, nullptr);
  40. mem->Unref();
  41. mem = nullptr;
  42. if (!status.ok())
  43. {
  44. // Reflect errors immediately so that conditions like full
  45. // file-systems cause the DB::Open() to fail.
  46. break;
  47. }
  48. }
  49. }
  50. delete file;

接着判断这个log文件是否可以继续使用,如果可以继续使用当前log文件,那么如果当前memtable不为空,则这个memtable也可以继续使用:

  1. // See if we should keep reusing the last log file.
  2. if (status.ok() && options_.reuse_logs && last_log && compactions == 0)
  3. {
  4. assert(logfile_ == nullptr);
  5. assert(log_ == nullptr);
  6. assert(mem_ == nullptr);
  7. uint64_t lfile_size;
  8. if (env_->GetFileSize(fname, &lfile_size).ok() &&
  9. env_->NewAppendableFile(fname, &logfile_).ok())
  10. {
  11. Log(options_.info_log, "Reusing old log %s \n", fname.c_str());
  12. log_ = new log::Writer(logfile_, lfile_size);
  13. logfile_number_ = log_number;
  14. if (mem != nullptr)
  15. {
  16. mem_ = mem;
  17. mem = nullptr;
  18. }
  19. else
  20. {
  21. // mem can be nullptr if lognum exists but was empty.
  22. mem_ = new MemTable(internal_comparator_);
  23. mem_->Ref();
  24. }
  25. }
  26. }

最后如果这个log文件不能继续使用,那么将当前的memtable进行compact写入level0:

  1. if (mem != nullptr)
  2. {
  3. // mem did not get reused; compact it.
  4. if (status.ok())
  5. {
  6. *save_manifest = true;
  7. status = WriteLevel0Table(mem, edit, nullptr);
  8. }
  9. mem->Unref();
  10. }
  11. return status;

LevelDB源码分析-Open的更多相关文章

  1. leveldb源码分析--SSTable之block

    在SSTable中主要存储数据的地方是data block,block_builder就是这个专门进行block的组织的地方,我们来详细看看其中的内容,其主要有Add,Finish和CurrentSi ...

  2. leveldb源码分析--WriteBatch

    从[leveldb源码分析--插入删除流程]和WriteBatch其名我们就很轻易的知道,这个是leveldb内部的一个批量写的结构,在leveldb为了提高插入和删除的效率,在其插入过程中都采用了批 ...

  3. leveldb源码分析--Key结构

    [注]本文参考了sparkliang的专栏的Leveldb源码分析--3并进行了一定的重组和排版 经过上一篇文章的分析我们队leveldb的插入流程有了一定的认识,而该文设计最多的又是Batch的概念 ...

  4. Leveldb源码分析--1

    coming from http://blog.csdn.net/sparkliang/article/details/8567602 [前言:看了一点oceanbase,没有意志力继续坚持下去了,暂 ...

  5. leveldb源码分析--日志

    我们知道在一个数据库系统中为了保证数据的可靠性,我们都会记录对系统的操作日志.日志的功能就是用来在系统down掉的时候对数据进行恢复,所以日志系统对一个要求可靠性的存储系统是极其重要的.接下来我们分析 ...

  6. leveldb源码分析之Slice

    转自:http://luodw.cc/2015/10/15/leveldb-02/ leveldb和redis这样的优秀开源框架都没有使用C++自带的字符串string,redis自己写了个sds,l ...

  7. LevelDB源码分析--Cache及Get查找流程

    本打算接下来分析version相关的概念,但是在准备的过程中看到了VersionSet的table_cache_这个变量才想起还有这样一个模块尚未分析,经过权衡觉得leveldb的version相对C ...

  8. leveldb源码分析--SSTable之TableBuilder

    上一篇文章讲述了SSTable的格式以后,本文结合源码解析SSTable是如何生成的. void TableBuilder::Add(const Slice& key, const Slice ...

  9. leveldb源码分析之内存池Arena

    转自:http://luodw.cc/2015/10/15/leveldb-04/ 这篇博客主要讲解下leveldb内存池,内存池很多地方都有用到,像linux内核也有个内存池.内存池的存在主要就是减 ...

  10. 【转】Leveldb源码分析——1

    先来看看Leveldb的基本框架,几大关键组件,如图1-1所示. Leveldb是一种基于operation log的文件系统,是Log-Structured-Merge Tree的典型实现.LSM源 ...

随机推荐

  1. Cent os6.5 安装python3.2

    1.CentOS6.5 安装Python 的依赖包 yum groupinstall "Development tools" yum install zlib-devel bzip ...

  2. Eclipse上传项目到Git

    转载自:http://blog.csdn.net/fan510988896/article/details/56277822 Git有和Svn类似的功能. 我们想使用Eclipse上传项目到自己的Gi ...

  3. 【转载】 Java中String类型的两种创建方式

    本文转载自 https://www.cnblogs.com/fguozhu/articles/2661055.html Java中String是一个特殊的包装类数据有两种创建形式: String s ...

  4. python3-基础8

    模块与包 什么是模块 模块就是一个包含了python定义和声明的文件,文件名就是模块名字加上.py的后缀. #在python中,模块的使用方式都是一样的,但其实细说的话,模块可以分为四个通用类别: 1 ...

  5. python基础知识9---字符串拼接,深浅拷贝,三元运算

    一.字符串格式化 Python的字符串格式化有两种方式: 百分号方式.format方式 百分号的方式相对来说比较老,而format方式则是比较先进的方式,企图替换古老的方式,目前两者并存.[PEP-3 ...

  6. idea导入svn项目

    起初和导入git项目一样,file - new - project from version control - ,这后面选 subversion. 在打开的 checkout from subver ...

  7. Kafka基本命令

    1.创建自定义的topic 在bin目录下执行: sh kafka-topics.sh --create --zookeeper hadoop01:2181 --replication-factor ...

  8. cookie和session的讲解

    php和js都是脚本语言: 客户端与服务器之间的交互,都是传输协议来进行交互的,客户向服务器发送的数据叫请求 request 服务器向客户端传输数据叫响应 response 他们之间都是无状态的: 无 ...

  9. 【原创】Mac book pro入手后,需要做哪些才能开始开展自动化测试工作

    2018国庆节,脑袋一热,入手了一台Mac book pro,从此掉坑到了这个异构的操作系统中,因为之前工作中接触了Windows.Linux.Unix等操作系统的诸多版本,基本的操作倒是不成问题,但 ...

  10. 转发:VB程序操作word表格(文字、图片)

    很多人都知道,用vb操作excel的表格非常简单,但是偏偏项目中碰到了VB操作word表格的部分,google.baidu搜爆了,都没有找到我需要的东西.到是搜索到了很多问这个问题的记录.没办法,索性 ...