leveldb将数据库的有关操作都定义在了DB类,它负责整个系统功能组件的连接和调用。是整个系统的脊柱。

level::DB是一个接口类,真正的实如今DBimpl类。

作者在文档impl.html中描写叙述了leveldb的实现。当中包含文件组织、compaction和recovery等等。

DBimpl的成员变量包含:字符比較器internal_comparator_、配置类options_、bool型状态量、string型DB库名、cache对象、memtable对象、versionset对象等等前面所说的组件。

前面的解说组件部分时。分散地介绍过leveldb的文件系统。这里以下来统一说明下创建一个DB,会在硬盘里生成一些什么样的文件,以下翻译自impl.html:

1 dbname/[0-9]+.log:

log文件包含了最新的db更新。每个entry更新都以append的方式追加到文件结尾。

2 dbname/[0-9]+.sst:db的sstable文件

Leveldb把sstable文件通过level的方式组织起来,从log文件里生成的sstable被放在level 0。

当level 0的sstable文件个数超过设置时,leveldb就把全部的level 0文件,以及有重合的level 1文件merge起来,组织成一个新的level 1文件。

3 dbname/MANIFEST-[0-9]+:DB元信息文件

它记录的是leveldb的元信息。比方DB使用的Comparator名,以及各SSTable文件的管理信息:如Level层数、文件名称、最小key和最大key等等。

4 dbname/CURRENT:记录当前正在使用的Manifest文件

它的内容就是当前的manifest文件名称;由于在LevleDb的执行过程中,随着Compaction的进行。新的SSTable文件被产生。老的文件被废弃。并生成新的Manifest文件来记载sstable的变动,而CURRENT则用来记录我们关心的Manifest文件。

5 dbname/log:系统的执行日志,和options_.info_log有关,记录系统的执行信息或者错误日志。

主要函数:

  1. Options SanitizeOptions(const std::string& dbname,
  2. const InternalKeyComparator* icmp,
  3. const InternalFilterPolicy* ipolicy,
  4. const Options& src)

option修正函数,将用户定义的option做一定的检查和修正,返回规范的option对象。

主要就是设置字符比較器。检查一些參数的设置(比方最大文件大小、写缓冲区的大小,sstable的block大小是否在规定值范围内)、建立log文件等等。

  1. Status DBImpl::NewDB() {
  2. VersionEdit new_db;
  3. new_db.SetComparatorName(user_comparator()->Name());
  4. new_db.SetLogNumber(0);
  5. new_db.SetNextFile(2);
  6. new_db.SetLastSequence(0);
  7. const std::string manifest = DescriptorFileName(dbname_, 1);
  8. WritableFile* file;
  9. Status s = env_->NewWritableFile(manifest, &file);
  10. if (!s.ok()) {
  11. return s;
  12. }
  13. {
  14. log::Writer log(file);
  15. std::string record;
  16. new_db.EncodeTo(&record);
  17. s = log.AddRecord(record);
  18. if (s.ok()) {
  19. s = file->Close();
  20. }
  21. }
  22. delete file;
  23. if (s.ok()) {
  24. // Make "CURRENT" file that points to the new manifest file.
  25. s = SetCurrentFile(env_, dbname_, 1);
  26. } else {
  27. env_->DeleteFile(manifest);
  28. }
  29. return s;
  30. }

初始化一个新的DB对象,主要创建一个manfest文件,并调用versionedit::encodeto写入新db的信息(如comparator,lognumder,nextfilenumber,sstable信息),此函数在open()操作中被调用,完毕创建DB的一步。

void DBImpl::DeleteObsoleteFiles()

依据i节点删除db中的文件,会对文件的类型和内容做一个推断,首先。正在compact的sstable不删,versionset中各个版本号下的sstable文件不删。当前的log和manfest文件不删。调用env_->DeleteFile删除文件。

  1. Status DBImpl::Recover(VersionEdit* edit)

DB恢复函数。基于前面介绍的文件系统

1.recover首先找到当前数据库dbname_路径下的current文件,參考函数CurrentFileName(dbname_)。文件错误或者不存在,恢复都无法继续进行),2.然后调用versionset::recover()。读取manfest文件,通过一个versionedit对象中间过渡,恢复出新的version。

3.遍历dbname_文件下的文件,对照当前版本号集合versions_中记录的sstable。假设缺失,输出缺失的文件i节点,recover失败。否则

恢复log文件(參考RecoverLogFile函数)

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

从log文件里逐条恢复entry,并写入新建立的memtable。并在合适的条件下(memtable大小大于写缓存下限:mem->ApproximateMemoryUsage() > options_.write_buffer_size)。写入level_0的sstable中(參考函数WriteLevel0Table)

  1. Status DBImpl::WriteLevel0Table(MemTable* mem, VersionEdit* edit,
  2. Version* base)

将memtable dump到磁盘,也就是level-0的sstable中。

1.首先产生一个新文件。并记录在文件描写叙述结构FileMetaData中

2.利用memtable的迭代器Iterator遍历memtable中的KV数据,构造sstable(參考函数BuildTable,还记得前面介绍table和block么,要对memtable的kv做进一步的打包。才干形成kv的磁盘形式)

3.把新的文件变化信息存储进versionedit,并记录这次compact的信息,主要是耗时和写入的sstable大小。

注:PickLevelForMemTableOutput函数,新的sstable定级。不能和同级的sstable有overlap。也不能和上级的sstable overlap太多(> kMaxGrandParentOverlapBytes)

WriteLevel0Table是函数CompactMemTable的核心。

leveldb中有且仅仅有一个进程单独做compact,当主线程触发compact。调用void DBImpl::MaybeScheduleCompaction()。假设compact正在执行或者DB正在退出。直接返回。检查version中是否存在须要compact。有则触发后台调度env_->schedele(…)

  1. void DBImpl::MaybeScheduleCompaction() {
  2. mutex_.AssertHeld();
  3. if (bg_compaction_scheduled_) {
  4. // Already scheduled
  5. } else if (shutting_down_.Acquire_Load()) {
  6. // DB is being deleted; no more background compactions
  7. } else if (!bg_error_.ok()) {
  8. // Already got an error; no more changes
  9. } else if (imm_ == NULL &&
  10. manual_compaction_ == NULL &&
  11. !versions_->NeedsCompaction()) {
  12. // No work to be done
  13. } else {
  14. bg_compaction_scheduled_ = true;
  15. env_->Schedule(&DBImpl::BGWork, this);
  16. }
  17. }

schedele把compact处理程序函数指针和db对象指针传入后台任务队列,BGWork 是compact处理函数。Schedule函数例如以下:

  1. void PosixEnv::Schedule(void (*function)(void*), void* arg) {
  2. PthreadCall("lock", pthread_mutex_lock(&mu_));
  3. // Start background thread if necessary
  4. if (!started_bgthread_) {
  5. started_bgthread_ = true;
  6. PthreadCall(
  7. "create thread",
  8. pthread_create(&bgthread_, NULL, &PosixEnv::BGThreadWrapper, this));
  9. }
  10. // If the queue is currently empty, the background thread may currently be
  11. // waiting.
  12. if (queue_.empty()) {
  13. PthreadCall("signal", pthread_cond_signal(&bgsignal_));
  14. }
  15. // Add to priority queue
  16. queue_.push_back(BGItem());
  17. queue_.back().function = function;
  18. queue_.back().arg = arg;
  19. PthreadCall("unlock", pthread_mutex_unlock(&mu_));
  20. }

将处理函数放入任务队列中,后台进程就能够不断地从queue_中取出任务函数,并执行。

实际compact处理进程是BackgroundCall和BackgroundCompaction。BackgroundCall完毕一些推断,条件符合则调用BackgroundCompaction,compact完毕后再次触发compact,反复上述过程。

  1. void DBImpl::BackgroundCall() {
  2. MutexLock l(&mutex_);
  3. assert(bg_compaction_scheduled_);
  4. if (shutting_down_.Acquire_Load()) {
  5. // No more background work when shutting down.
  6. } else if (!bg_error_.ok()) {
  7. // No more background work after a background error.
  8. } else {
  9. BackgroundCompaction();
  10. }
  11. bg_compaction_scheduled_ = false;
  12. // Previous compaction may have produced too many files in a level,
  13. // so reschedule another compaction if needed.
  14. MaybeScheduleCompaction();
  15. bg_cv_.SignalAll();
  16. }

实际compact流程:

  1. void DBImpl::BackgroundCompaction() {
  2. mutex_.AssertHeld();
  3. //immutable先compact
  4. if (imm_ != NULL) {
  5. CompactMemTable();
  6. return;
  7. }
  8. //针对人为指定compact的key-range
  9. Compaction* c;
  10. bool is_manual = (manual_compaction_ != NULL);
  11. InternalKey manual_end;
  12. if (is_manual) {
  13. ManualCompaction* m = manual_compaction_;
  14. c = versions_->CompactRange(m->level, m->begin, m->end);
  15. m->done = (c == NULL);
  16. if (c != NULL) {
  17. manual_end = c->input(0, c->num_input_files(0) - 1)->largest;
  18. }
  19. Log(options_.info_log,
  20. "Manual compaction at level-%d from %s .. %s; will stop at %s\n",
  21. m->level,
  22. (m->begin ? m->begin->DebugString().c_str() : "(begin)"),
  23. (m->end ?
  24. m->end->DebugString().c_str() : "(end)"),
  25. (m->done ? "(end)" : manual_end.DebugString().c_str()));
  26. } else {
  27. //确定须要compact的level-n和sstable
  28. c = versions_->PickCompaction();
  29. }
  30. Status status;
  31. if (c == NULL) {
  32. // Nothing to do
  33. } else if (!is_manual && c->IsTrivialMove()) {
  34. // Move file to next level
  35. assert(c->num_input_files(0) == 1);
  36. FileMetaData* f = c->input(0, 0);
  37. c->edit()->DeleteFile(c->level(), f->number);
  38. c->edit()->AddFile(c->level() + 1, f->number, f->file_size,
  39. f->smallest, f->largest);
  40. status = versions_->LogAndApply(c->edit(), &mutex_);
  41. if (!status.ok()) {
  42. RecordBackgroundError(status);
  43. }
  44. VersionSet::LevelSummaryStorage tmp;
  45. Log(options_.info_log, "Moved #%lld to level-%d %lld bytes %s: %s\n",
  46. static_cast<unsigned long long>(f->number),
  47. c->level() + 1,
  48. static_cast<unsigned long long>(f->file_size),
  49. status.ToString().c_str(),
  50. versions_->LevelSummary(&tmp));
  51. } else {
  52. CompactionState* compact = new CompactionState(c);
  53. status = DoCompactionWork(compact);
  54. if (!status.ok()) {
  55. RecordBackgroundError(status);
  56. }
  57. CleanupCompaction(compact);
  58. c->ReleaseInputs();
  59. DeleteObsoleteFiles();
  60. }
  61. delete c;
  62. if (status.ok()) {
  63. // Done
  64. } else if (shutting_down_.Acquire_Load()) {
  65. // Ignore compaction errors found during shutting down
  66. } else {
  67. Log(options_.info_log,
  68. "Compaction error: %s", status.ToString().c_str());
  69. }
  70. if (is_manual) {
  71. ManualCompaction* m = manual_compaction_;
  72. if (!status.ok()) {
  73. m->done = true;
  74. }
  75. if (!m->done) {
  76. // We only compacted part of the requested range. Update *m
  77. // to the range that is left to be compacted.
  78. m->tmp_storage = manual_end;
  79. m->begin = &m->tmp_storage;
  80. }
  81. manual_compaction_ = NULL;
  82. }
  83. }

1.假设存在immutable memtable。将其dump成sstable,完毕返回。

2.假设是外部触发的compact,依据manual_compaction指定的level/start_key/end_key,选出compaction(VersionSet::CompactRange())

3.假设不是manual compact。则依据db当前状态,选出compaction(VersionSet::PickCompaction()),考虑到level sstable的均衡性,提高查找效率。

class compaction用于记录compact信息,包含compact的level和输入sstable文件等等,參见version_set.h。

4.对于非manual compact而且选出的sstable都处于level-n且不会造成过多的GrandparentOverrlap(Compaction::IsTrivialMove()),简单处理,将这些sstable推到level-n+1,更新db元信息就可以(VersionSet::LogAndApply())。

5.其它情况,则一律依据确定出的Compaction,做详细的compact处理(DBImpl::DoCompactionWork()),最后做异常情况的清理(DBImpl::CleanupCompaction())。

DBimpl::DoCompactionWork()。实际的compact过程就是对多个已经排序的sstable做一次merge排序。丢弃掉同样的Key以及删除的数据。

  1. Status DBImpl::DoCompactionWork(CompactionState* compact) {
  2. const uint64_t start_micros = env_->NowMicros();
  3. //immutable compact时计时用
  4. int64_t imm_micros = 0; // Micros spent doing imm_ compactions
  5. Log(options_.info_log, "Compacting %d@%d + %d@%d files",
  6. compact->compaction->num_input_files(0),
  7. compact->compaction->level(),
  8. compact->compaction->num_input_files(1),
  9. compact->compaction->level() + 1);
  10. assert(versions_->NumLevelFiles(compact->compaction->level()) > 0);
  11. assert(compact->builder == NULL);
  12. assert(compact->outfile == NULL);
  13. if (snapshots_.empty()) {
  14. compact->smallest_snapshot = versions_->LastSequence();
  15. } else {
  16. compact->smallest_snapshot = snapshots_.oldest()->number_;
  17. }
  18. // Release mutex while we're actually doing the compaction work
  19. mutex_.Unlock();
  20. //将选出的compaction中的sstable构造MergingIterator
  21. //对于level-0做归并排序。其它level的sstable做一个连接他们的iterator
  22. Iterator* input = versions_->MakeInputIterator(compact->compaction);
  23. //定位到每个sstable的first,后面将遍历input sstable的entry
  24. input->SeekToFirst();
  25. Status status;
  26. ParsedInternalKey ikey;
  27. std::string current_user_key;
  28. bool has_current_user_key = false;
  29. SequenceNumber last_sequence_for_key = kMaxSequenceNumber;
  30. for (; input->Valid() && !shutting_down_.Acquire_Load(); ) {
  31. // Prioritize immutable compaction work
  32. //优先完毕immutable的compact
  33. if (has_imm_.NoBarrier_Load() != NULL) {
  34. const uint64_t imm_start = env_->NowMicros();
  35. mutex_.Lock();
  36. if (imm_ != NULL) {
  37. CompactMemTable();
  38. bg_cv_.SignalAll(); // Wakeup MakeRoomForWrite() if necessary
  39. }
  40. mutex_.Unlock();
  41. imm_micros += (env_->NowMicros() - imm_start);
  42. }
  43. Slice key = input->key();
  44. //假设当前于grandparent层产生overlap的size超过阈值,马上结束当前写入的table的构造。写入磁盘
  45. if (compact->compaction->ShouldStopBefore(key) &&
  46. compact->builder != NULL) {
  47. status = FinishCompactionOutputFile(compact, input);
  48. if (!status.ok()) {
  49. break;
  50. }
  51. }
  52. // Handle key/value, add to state, etc.
  53. //key舍弃标志位
  54. bool drop = false;
  55. //key解析错误,放弃
  56. if (!ParseInternalKey(key, &ikey)) {
  57. // Do not hide error keys
  58. current_user_key.clear();
  59. has_current_user_key = false;
  60. last_sequence_for_key = kMaxSequenceNumber;
  61. } else {
  62. //key与前面的key反复。丢弃
  63. if (!has_current_user_key ||
  64. user_comparator()->Compare(ikey.user_key,
  65. Slice(current_user_key)) != 0) {
  66. // First occurrence of this user key
  67. current_user_key.assign(ikey.user_key.data(), ikey.user_key.size());
  68. has_current_user_key = true;
  69. last_sequence_for_key = kMaxSequenceNumber;
  70. }
  71. //key是删除类型,丢弃
  72. if (last_sequence_for_key <= compact->smallest_snapshot) {
  73. // Hidden by an newer entry for same user key
  74. drop = true; // (A)
  75. } else if (ikey.type == kTypeDeletion &&
  76. ikey.sequence <= compact->smallest_snapshot &&
  77. compact->compaction->IsBaseLevelForKey(ikey.user_key)) {
  78. // For this user key:
  79. // (1) there is no data in higher levels
  80. // (2) data in lower levels will have larger sequence numbers
  81. // (3) data in layers that are being compacted here and have
  82. // smaller sequence numbers will be dropped in the next
  83. // few iterations of this loop (by rule (A) above).
  84. // Therefore this deletion marker is obsolete and can be dropped.
  85. drop = true;
  86. }
  87. last_sequence_for_key = ikey.sequence;
  88. }
  89. #if 0
  90. Log(options_.info_log,
  91. " Compact: %s, seq %d, type: %d %d, drop: %d, is_base: %d, "
  92. "%d smallest_snapshot: %d",
  93. ikey.user_key.ToString().c_str(),
  94. (int)ikey.sequence, ikey.type, kTypeValue, drop,
  95. compact->compaction->IsBaseLevelForKey(ikey.user_key),
  96. (int)last_sequence_for_key, (int)compact->smallest_snapshot);
  97. #endif
  98. if (!drop) {
  99. //假设output sstable未生成。构造新的tablebuilder
  100. // Open output file if necessary
  101. if (compact->builder == NULL) {
  102. status = OpenCompactionOutputFile(compact);
  103. if (!status.ok()) {
  104. break;
  105. }
  106. }
  107. //第一次写入的key作为output的smallest key
  108. if (compact->builder->NumEntries() == 0) {
  109. compact->current_output()->smallest.DecodeFrom(key);
  110. }
  111. //新的key写入时,更新largest key,并add进table
  112. compact->current_output()->largest.DecodeFrom(key);
  113. compact->builder->Add(key, input->value());
  114. // Close output file if it is big enough
  115. //当前sstable太大了就结束table构造
  116. if (compact->builder->FileSize() >=
  117. compact->compaction->MaxOutputFileSize()) {
  118. status = FinishCompactionOutputFile(compact, input);
  119. if (!status.ok()) {
  120. break;
  121. }
  122. }
  123. }
  124. //下一个key
  125. input->Next();
  126. }
  127. if (status.ok() && shutting_down_.Acquire_Load()) {
  128. status = Status::IOError("Deleting DB during compaction");
  129. }
  130. if (status.ok() && compact->builder != NULL) {
  131. status = FinishCompactionOutputFile(compact, input);
  132. }
  133. if (status.ok()) {
  134. status = input->status();
  135. }
  136. delete input;
  137. input = NULL;
  138. //将此次compact的信息增加dbimpl::status_
  139. CompactionStats stats;
  140. stats.micros = env_->NowMicros() - start_micros - imm_micros;
  141. for (int which = 0; which < 2; which++) {
  142. for (int i = 0; i < compact->compaction->num_input_files(which); i++) {
  143. stats.bytes_read += compact->compaction->input(which, i)->file_size;
  144. }
  145. }
  146. for (size_t i = 0; i < compact->outputs.size(); i++) {
  147. stats.bytes_written += compact->outputs[i].file_size;
  148. }
  149. mutex_.Lock();
  150. stats_[compact->compaction->level() + 1].Add(stats);
  151. if (status.ok()) {
  152. status = InstallCompactionResults(compact);
  153. }
  154. if (!status.ok()) {
  155. RecordBackgroundError(status);
  156. }
  157. VersionSet::LevelSummaryStorage tmp;
  158. Log(options_.info_log,
  159. "compacted to: %s", versions_->LevelSummary(&tmp));
  160. return status;
  161. }

leveldb学习:DBimpl的更多相关文章

  1. leveldb 学习记录(三) MemTable 与 Immutable Memtable

    前文: leveldb 学习记录(一) skiplist leveldb 学习记录(二) Slice 存储格式: leveldb数据在内存中以 Memtable存储(核心结构是skiplist 已介绍 ...

  2. LevelDB学习笔记 (3): 长文解析memtable、跳表和内存池Arena

    LevelDB学习笔记 (3): 长文解析memtable.跳表和内存池Arena 1. MemTable的基本信息 我们前面说过leveldb的所有数据都会先写入memtable中,在leveldb ...

  3. leveldb 学习记录(四) skiplist补与变长数字

    在leveldb 学习记录(一) skiplist 已经将skiplist的插入 查找等操作流程用图示说明 这里在介绍 下skiplist的代码 里面有几个模块 template<typenam ...

  4. leveldb 学习记录(四)Log文件

    前文记录 leveldb 学习记录(一) skiplistleveldb 学习记录(二) Sliceleveldb 学习记录(三) MemTable 与 Immutable Memtablelevel ...

  5. leveldb学习:Versionedit和Versionset

    VersionEdit: compact过程中会有一系列改变当前Version的操作(FileNumber添加.删除input的sstable,添加输出的sstable).为了缩小version切换的 ...

  6. LevelDB学习笔记 (1):初识LevelDB

    LevelDB学习笔记 (1):初识LevelDB 1. 写在前面 1.1 什么是levelDB LevelDB就是一个由Google开源的高效的单机Key/Value存储系统,该存储系统提供了Key ...

  7. LevelDB 学习笔记1:布隆过滤器

    LevelDB 学习笔记1:布隆过滤器 底层是位数组,初始都是 0 插入时,用 k 个哈希函数对插入的数字做哈希,并用位数组长度取余,将对应位置 1 查找时,做同样的哈希操作,查看这些位的值 如果所有 ...

  8. LevelDB 学习笔记2:合并

    LevelDB 学习笔记2:合并 部分图片来自 RocksDB 文档 Minor Compaction 将内存数据库刷到硬盘的过程称为 minor compaction 产出的 L0 层的 sstab ...

  9. leveldb 学习。

    1)大概浏览了leveldb文档的介绍.本想逐步看代码,想想还是自己先实现一个看看如何改进. 2)完成了一个非常丑陋的初版,但是还是比初初版有进步. 3)key value的数据库,不允许有key重复 ...

随机推荐

  1. 免费开源《OdooERP系统部署架构指南》试读:第一章 Odoo架构概述

    文/开源智造联合创始人老杨 本文来自<OdooERP系统部署架构指南>的试读章节.书籍尚未出版,请勿转载.欢迎您反馈阅读意见. 从web浏览器到PostgreSQL,多层与其他层交互以处理 ...

  2. solr中的schema.xml(managed-schema)文件解读

    solr 7.2.1版本managed-schema文件示例 <uniqueKey>id</uniqueKey> 唯一键字段,solr对每一个文档都赋予一个唯一标识符字段,避免 ...

  3. 创意、实现和合作:一次原创H5的尝试

    3月的某一天需求同学说:我们想做一个爆款的回流H5. 好的事实上并没有这么夸张. 不过我们确实是第一次真正意义上做这样一个以互动展示为主要形式.以传播和拉回流为主要目的的H5. 虽然最后也没有成为真正 ...

  4. ajax传递参数给springmvc

    下面看一些传参的例子,基本涵盖了大部分的参数传递类型

  5. xtu DP Training C.炮兵阵地

    炮兵阵地 Time Limit: 2000ms Memory Limit: 65536KB This problem will be judged on PKU. Original ID: 11856 ...

  6. python024 Python3 实例

    Python3 实例 以下实例在 Python3.4.3 版本下测试通过: Python Hello World 实例 Python 数字求和 Python 平方根 Python 二次方程 Pytho ...

  7. Hibernate 批处理(batch inserts, updates and deletes)

    总结:hibernate在进行批量处理不给力的主要原因就是Session中存在缓存,而hibernate的机制就是通过session中的一级缓存去同步数据库,所以当进行批量处理时,缓存中保存的数据量很 ...

  8. 【BZOJ1717&POJ3261】Milk Patterns(后缀数组,二分)

    题意:求字符串的可重叠的k次最长重复子串 n<=20000 a[i]<=1000000 思路:后缀数组+二分答案x,根据height分组,每组之间的height>=x 因为可以重叠, ...

  9. BZOJ1703: [Usaco2007 Mar]Ranking the Cows 奶牛排名

    n<=1000头牛各有一个未知值Ai,已知m<=10000条形如Ax>Ay的不等关系,求将整个序列排序的最少比较次数. Aa>Ab,Ab>Ac -------> A ...

  10. mysql获取行号的方法

    1.不排序 语句: ) ) ) b,bigquestion 结果:  2.排序的 语句 ) ) ) b,bigquestion order by bigquestion.bigQuestionSequ ...