leveldb深度剖析-压缩流程(3)

本篇是压缩流程中最后一篇,介绍DoCompactionWork方法。压缩主要工作是什么呢?剔除无效数据。什么是无效数据呢?就是被标记为删除的数据,我们要在这个流程中从磁盘中删除掉。下面来看一下具体是如何进行删除的。

一、迭代器

该函数比较长,这里分段介绍该函数。

/**
 * 执行压缩
 * @param compact 压缩信息
 */
Status DBImpl::DoCompactionWork(CompactionState* compact) {
  const uint64_t start_micros = env_->NowMicros();
  int64_t imm_micros = 0;  // Micros spent doing imm_ compactions

  Log(options_.info_log,  "Compacting %d@%d + %d@%d files",
      compact->compaction->num_input_files(0),
      compact->compaction->level(),
      compact->compaction->num_input_files(1),
      compact->compaction->level() + 1);

  assert(versions_->NumLevelFiles(compact->compaction->level()) > 0);
  assert(compact->builder == NULL);
  assert(compact->outfile == NULL);

  if (snapshots_.empty()) {//如果没有快照,则重复的旧k/v数据都可以删掉
    compact->smallest_snapshot = versions_->LastSequence();
  } else {//如果有快照,则只有sequenceNumber小于最老的快照的sequenceNumber的旧k/v数据才可以删掉
    compact->smallest_snapshot = snapshots_.oldest()->number_;
  }

  // Release mutex while we're actually doing the compaction work
  mutex_.Unlock();

  //创建迭代器 这个创建迭代器的过程非常重要  为下面判断数据是否有效 起到至关重要的作用
  Iterator* input = versions_->MakeInputIterator(compact->compaction);
  input->SeekToFirst();
  Status status;
  ParsedInternalKey ikey;
  std::string current_user_key;//当前记录user key
  bool has_current_user_key = false;//是否有当前user key 和current_user_key成对出现
  SequenceNumber last_sequence_for_key = kMaxSequenceNumber;

说明:

1)这部分最重要的内容就是创建了一个迭代器Iterator,这个迭代器非常的关键,是将所有的key按照从小到大的顺序进行排序。切记:创建完迭代器后就已经按照key的顺序进行排序了。 

2)上面几个变了是处理记录是否真正删除主要控制逻辑。

二、循环遍历key并删除数据

for (; input->Valid() && !shutting_down_.Acquire_Load(); ) {//循环遍历
    // Prioritize immutable compaction work
    if (has_imm_.NoBarrier_Load() != NULL) {
      const uint64_t imm_start = env_->NowMicros();
      mutex_.Lock();
      if (imm_ != NULL) {
        CompactMemTable();//优先压缩immutable memtable
        bg_cv_.SignalAll();  // Wakeup MakeRoomForWrite() if necessary
      }
      mutex_.Unlock();
      imm_micros += (env_->NowMicros() - imm_start);
    }

    Slice key = input->key();//InternalKey
    if (compact->compaction->ShouldStopBefore(key) &&
        compact->builder != NULL) {
      status = FinishCompactionOutputFile(compact, input);
      if (!status.ok()) {
        break;
      }
    }
    ...
  }//end for

这部分逻辑比较简单,就是从迭代器中获取一条数据,这里的input->key()为InternalKey对象,并非是user key。下面这部分是关键


    // Handle key/value, add to state, etc.
    bool drop = false;//代表记录是否要被删除 true 要从数据库中删除
    if (!ParseInternalKey(key, &ikey)) {//解析key 失败
      // Do not hide error keys
      current_user_key.clear();
      has_current_user_key = false;
      last_sequence_for_key = kMaxSequenceNumber;
    } else {//解析key 成功
      /**
       * 经过排序之后 相同的user key被放到迭代器 组织到一起了(相同user key放到一起)
       * current_user_key 保存上次user key
       * ikey.user_key是本次循环中 处理的user key
       * 如果compare返回非0 表示这两个user key不一样,返回为0则说明两个key相同,
       * 那么相同说明了什么呢? 说明上一条是标记为Delete的数据,而这条记录是原始添加数据,所以
       * 这条记录是需要删除的。
       */
      if (!has_current_user_key ||
          user_comparator()->Compare(ikey.user_key,
                                     Slice(current_user_key)) != 0) {
        // First occurrence of this user key
        current_user_key.assign(ikey.user_key.data(), ikey.user_key.size());
        has_current_user_key = true;//表示第一次出现 设置为current user key
        last_sequence_for_key = kMaxSequenceNumber; //设置为最大序列号
      }

      if (last_sequence_for_key <= compact->smallest_snapshot) {
        // Hidden by an newer entry for same user key
        drop = true;    // (A)
      } else if (ikey.type == kTypeDeletion &&
                 ikey.sequence <= compact->smallest_snapshot &&
                 compact->compaction->IsBaseLevelForKey(ikey.user_key)) {
        // IsBaseLevelForKey函数返回true表示user_key不存在于高层中   false 表示存在于高层中
        // For this user key 对于当前user key来说:
        // (1) there is no data in higher levels 它不存在高层ldb文件中
        // (2) data in lower levels will have larger sequence numbers
        // (3) data in layers that are being compacted here and have
        //     smaller sequence numbers will be dropped in the next
        //     few iterations of this loop (by rule (A) above).
        // Therefore this deletion marker is obsolete and can be dropped.
        //要删除该记录 && 序列号比之前数据库中还要小 && 当前记录不存在于高层次文件中 所以标记为删除
        drop = true;
      }

      last_sequence_for_key = ikey.sequence;
    }
#if 0
    Log(options_.info_log,
        "  Compact: %s, seq %d, type: %d %d, drop: %d, is_base: %d, "
        "%d smallest_snapshot: %d",
        ikey.user_key.ToString().c_str(),
        (int)ikey.sequence, ikey.type, kTypeValue, drop,
        compact->compaction->IsBaseLevelForKey(ikey.user_key),
        (int)last_sequence_for_key, (int)compact->smallest_snapshot);
#endif

    if (!drop) {//不是删除操作 写入文件
      // Open output file if necessary
      if (compact->builder == NULL) {
        status = OpenCompactionOutputFile(compact);
        if (!status.ok()) {
          break;
        }
      }
      if (compact->builder->NumEntries() == 0) {
        compact->current_output()->smallest.DecodeFrom(key);
      }
      compact->current_output()->largest.DecodeFrom(key);
      compact->builder->Add(key, input->value());

      // Close output file if it is big enough
      if (compact->builder->FileSize() >=
          compact->compaction->MaxOutputFileSize()) {
        status = FinishCompactionOutputFile(compact, input);
        if (!status.ok()) {
          break;
        }
      }
    }

    input->Next(); //获取下一条记录

说明:

1)局部变量drop为true表示记录从物理文件中删除,反之不需要删除掉。

2) ParseInternalKey方法是用于解析InternalKey对象,正常场景会进入else分支。

3)上面提到了创建的迭代器会对InternalKey进行排序,那么相同的user-key就会相邻,那么带有删除标记的一定在前面(删除标记为0,添加标记为1)。所以在处理是删除标记时直接删除掉了,但是原始数据应该怎么删除呢?

4)代码注释中有这样的内容:

      /**
       * 经过排序之后 相同的user key被放到迭代器 组织到一起了(相同user key放到一起)
       * current_user_key 保存上次user key
       * ikey.user_key是本次循环中 处理的user key
       * 如果compare返回非0 表示这两个user key不一样,返回为0则说明两个key相同,
       * 那么相同说明了什么呢? 说明上一条是标记为Delete的数据,而这条记录是原始添加数据,所以
       * 这条记录是需要删除的。
       */
      if (!has_current_user_key || user_comparator()->Compare(ikey.user_key, Slice(current_user_key)) != 0)

5)写入文件这里不在介绍了。

三、记录统计信息


  if (status.ok() && shutting_down_.Acquire_Load()) {
    status = Status::IOError("Deleting DB during compaction");
  }
  if (status.ok() && compact->builder != NULL) {
    status = FinishCompactionOutputFile(compact, input);
  }
  if (status.ok()) {
    status = input->status();
  }
  delete input;
  input = NULL;

  // 统计信息
  CompactionStats stats;
  stats.micros = env_->NowMicros() - start_micros - imm_micros;
  for (int which = 0; which < 2; which++) {
    for (int i = 0; i < compact->compaction->num_input_files(which); i++) {
      stats.bytes_read += compact->compaction->input(which, i)->file_size;
    }
  }
  for (size_t i = 0; i < compact->outputs.size(); i++) {
    stats.bytes_written += compact->outputs[i].file_size;
  }

  mutex_.Lock();
  stats_[compact->compaction->level() + 1].Add(stats);

  if (status.ok()) {
    status = InstallCompactionResults(compact);
  }
  if (!status.ok()) {
    RecordBackgroundError(status);
  }
  VersionSet::LevelSummaryStorage tmp;
  Log(options_.info_log,
      "compacted to: %s", versions_->LevelSummary(&tmp));
  return status;
}// end function

这里部分就是统计信息,这里不在深入展开了,大家可自行阅读相关源码 

四、总结

至此压缩整体流程介绍完毕了。由此可见,leveldb对于压缩流程做了最大化的性能提升,里面涉及比较复杂的逻辑,需要我们后续慢慢体味。下一篇介绍查询接口。

根据引用,你提到了pip show jsonpath的安装成功。根据这个引用,我可以确认jsonpath已经成功安装在你的Python环境中。 至于你提到的"windows python LevelDB安装 metadata-generation-failed",我没有找到相关的引用来回答这个问题。但是我可以给你一些建议来解决这个问题。 首先,确保你已经正确地安装了LevelDB以及与之相关的依赖包。你可以尝试使用pip install命令来安装LevelDB。如果出现metadata generation failed的错误,可能是由于网络问题或者软件包源的问题导致的。你可以尝试使用其他的软件包源或者使用代理来解决这个问题。 另外,你还可以尝试使用conda来安装LevelDB,它是一个流行的Python包管理器,可以管理Python包及其依赖项。你可以在Anaconda官方网站上找到更多关于conda的信息和安装指南。 最后,如果上述方法都没有解决你的问题,我建议你检查一下你的操作系统和Python版本是否与LevelDB兼容。有时候,特定的软件包可能只能在特定的操作系统或者Python版本上正常工作。 希望这些建议对你有帮助,如果你还有其他问题,请随时提问。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [python38安装jsonpath失败问题解决](https://blog.csdn.net/qq_27371025/article/details/125855179)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] - *2* *3* [The Log: What every software engineer should know about real-time data's unifying abstraction](https://blog.csdn.net/iloveu8780/article/details/80097101)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值