tair是一个分布式KV存储引擎,当新增机器或者有机器down掉的时候,tair的dataserver会根据ConfigServer生成的新的对照表进行数据的迁移和清理。在数据清理的过程中就用到了在tair中新增的Compaction方式——CompactRangeSelfLevel,顾名思义,这个CompactRangeSelfLevel就是对自己所在(指定)的Level进行一定Key范围的Compaction然后将生成的输出文件也写入到自己所在的Level而不是父层(L + 1)。下面我们来对这个CompactRangeSelfLevel进行分析。

  1. // compact filenumber 小于limit的key 在 [begin, end)范围内的 sstable
  2. // compact的时候只有level 0 SSTable会输出到 level 1, 其他的level都是输入输出在同一个level
  3. Status DBImpl::CompactRangeSelfLevel(
  4. uint64_t limit_filenumber,
  5. const Slice* begin,
  6. const Slice* end) {
  7. // 初始化一个MannualCompaction对象
  8. manual.limit_filenumber = limit_filenumber;
  9. manual.bg_compaction_func = &DBImpl::BackgroundCompactionSelfLevel;
  10. // use TimedCond() 防止丢失唤醒信号
  11. // 每个层级逐次schedule manualcompaction
  12. for (int level = ; level < config::kNumLevels && manual.compaction_status.ok(); ++level) {
  13. ManualCompaction each_manual = manual;
  14. each_manual.level = level;
  15. while (each_manual.compaction_status.ok() && !each_manual.done) {
  16. // still have compaction running 就等待
  17. while (bg_compaction_scheduled_) {
  18. bg_cv_.TimedWait(timed_us);
  19. }
  20. manual_compaction_ = &each_manual;
  21. MaybeScheduleCompaction();
  22. while (manual_compaction_ == &each_manual) {
  23. bg_cv_.TimedWait(timed_us);
  24. }
  25. }
  26. manual.compaction_status = each_manual.compaction_status;
  27. }
  28. return manual.compaction_status;
  29. }

MaybeScheduleCompaction主要是判断是否新启动一个后台的Compaction线程,主要是以是否有Compaction的任务和是否已经有Compaction线程已经在运行为依据,这个函数已经在前面专门解释Compaction的文章中进行了分析,这里不再介绍。我们详细分析一下真正进行工作的BackgroundCompactionSelfLevel函数

  1. void DBImpl::BackgroundCompactionSelfLevel() {
  2. do {
  3. // level-0 不对 filenumber 进行限制
  4. /* CompactRangeOneLevel故名思议将该level中的所有符合filenumber 小于limit
  5. key 在 [begin, end)范围内的 sstable找出来 */
  6. c = versions_-> CompactRangeOneLevel(m->level,
  7. m->level > ? m->limit_filenumber : ~(static_cast<uint64_t>()),
  8. m->begin, m->end);
  9.  
  10. if (NULL == c) { // no compact for this level
  11. m->done = true; // done all.
  12. break;
  13. }
  14. // 记录下manual Compaction的结束key
  15. manual_end = c->input(, c->num_input_files() - )->largest;
  16.  
  17. CompactionState* compact = new CompactionState(c);
  18. status = DoCompactionWorkSelfLevel(compact); // 真正进行Compaction的函数
  19. CleanupCompaction(compact);
  20. c->ReleaseInputs();
  21. DeleteObsoleteFiles();
  22. delete c;
  23. if (shutting_down_.Acquire_Load()) {
  24. // Ignore compaction errors found during shutting down
  25. } else if (!status.ok()) {
  26. m->compaction_status = status; // save error
  27. if (bg_error_.ok()) { // no matter paranoid_checks
  28. bg_error_ = status;
  29. }
  30. break; // exit once fail.
  31. }
  32. } while (false);
  33. if (!m->done) {
  34. // We only compacted part of the requested range. Update *m
  35. // to the range that is left to be compacted.
  36. m->tmp_storage = manual_end;
  37. m->begin = &m->tmp_storage;
  38. }
  39. // Mark it as done
  40. manual_compaction_ = NULL;
  41. }

这里DoCompactionWorkSelfLevel是真正进行KV读取和Compaction的地方,然而我们这里并不打算对其进行详细的分析,因为通过对比我们知道其主题过程和DoCompactionWork相同,只是在一些细微的判断方式和处理方式上稍微有所不同。具体的DoCompactionWork的过程请参考《leveldb源码分析--SSTable之Compaction》,我们下面通过对比 差异之处的方式让大家理解DoCompactionWorkSelfLevel的实际处理过程。

DoCompactionWorkSelfLevel 和 DoCompactionWork基本相同,只是流程上少了几个判断:

1. DoCompactionWorkSelfLevel 遍历到key以后不需要进行ShouldStopBefore的判断,因为这个是判断是否跟L + 2层有过多的重叠,这里selflevel是输出到当前层,所以必然不会影响跟L+ 2层的重叠情况;

  1. Slice key = input->key();
  2. // if (compact->compaction->ShouldStopBefore(key) &&
  3. // compact->builder != NULL) {
  4. // status = FinishCompactionOutputFile(compact, input);
  5. // if (!status.ok()) {
  6. // break;
  7. // }
  8. // }
  9. // 注释掉的地方即是CompactionWorkSelfLevel减少的部分

2. 是否drop的时候少了seq<= smallest_snapshot && (type == Deletion || ShouldDrop) && IsBaseLevelForKey(ikey)) 为drop,也是因为当前层的Compaction,而IsBaseLevelForKey是判断的L + 2层以上有无该key的相关值,这里如果要加上判断就应该是将 L+1 层也包含在判断范围内。

  1. } else if (ikey.sequence <= compact->smallest_snapshot &&
  2. (ikey.type == kTypeDeletion || // deleted or ..
  3. user_comparator()->ShouldDropMaybe(ikey.user_key.data(),
  4. ikey.sequence, expired_end_time)) &&
  5. // .. user-defined should drop(maybe),
  6. // based on some condition(eg. this key only has this update.).
  7. compact->compaction->IsBaseLevelForKey(ikey.user_key)) {
  8. // For this user key:
  9. // (1) there is no data in higher levels
  10. // (2) data in lower levels will have larger sequence numbers
  11. // (3) data in layers that are being compacted here and have
  12. // smaller sequence numbers will be dropped in the next
  13. // few iterations of this loop (by rule (A) above).
  14. // Therefore this deletion marker is obsolete and can be dropped.
  15. drop = true;
  16. }

3. 在InstallCompactionResults 时第二个参数传入的是false,这样这个函数将新生成的SSTable放入当前层而不是L+ 1层

  1. status = InstallCompactionResults(compact);
  2. // 修改为:
  3. status = InstallCompactionResults(compact, false); // output files is in current level, not level + 1

另外这里顺便提一下tair中的leveldb是对google开源的leveldb也有了一定的修改,比如添加expire等特性,在tair中comparator就添加了三个接口函数。而在tair中实现了两个这样的comparator分别是NumericalComparatorImpl和BitcmpLdbComparatorImpl,我们这里以BitcmpLdbComparatorImpl为例进行一下简单的介绍其功能。

  1. // 判断这个key是否在需要回收的bucket中,如果是就返回true,那么Compaction的时候直接删除(即回收掉)
  2. virtual bool ShouldDrop(const char* key, int64_t sequence, uint32_t now = ) const { return false;}
  3. // 根据expire_time判断这个key是否已经过期,如过期则返回true
  4. virtual bool ShouldDropMaybe(const char* key, int64_t sequence, uint32_t now = ) const { return false;}
  5. // start_key和key是否依旧属于同一个bucket,是的放回false,否则返回true
  6. virtual bool ShouldStopBefore(const Slice& start_key, const Slice& key) const { return false;}

有了这三个函数以后tair的ldb引擎就可以在Compaction的时候对key进行回收和判断是否写入同一个SSTable中,比如最直接的Compaction的时候如果ShouldDrop返回true那么直接标记这个key为drop不写入到新的SSTable中;而ShouldStopBefore则被用在是否生成新的SSTable文件,如果返回true则结束当前文件的写入生成下一个SSTable,这样就可以将不同的bucket写入到不同的SSTable文件中了。

tair源码分析——leveldb新增的CompactRangeSelfLevel过程的更多相关文章

  1. tair源码分析——leveldb存储引擎使用

    分析完leveldb以后,接下来的时间准备队tair的源码进行阅读和分析.我们刚刚分析完了leveldb而在tair中leveldb是其几大存储引擎之一,所以我们这里首先从tair对leveldb的使 ...

  2. SpringBoot源码分析之SpringBoot的启动过程

    SpringBoot源码分析之SpringBoot的启动过程 发表于 2017-04-30   |   分类于 springboot  |   0 Comments  |   阅读次数 SpringB ...

  3. Envoy 源码分析--程序启动过程

    目录 Envoy 源码分析--程序启动过程 初始化 main 入口 MainCommon 初始化 服务 InstanceImpl 初始化 启动 main 启动入口 服务启动流程 LDS 服务启动流程 ...

  4. Spring源码分析专题 —— IOC容器启动过程(上篇)

    声明 1.建议先阅读<Spring源码分析专题 -- 阅读指引> 2.强烈建议阅读过程中要参照调用过程图,每篇都有其对应的调用过程图 3.写文不易,转载请标明出处 前言 关于 IOC 容器 ...

  5. Spring源码分析之Bean的创建过程详解

    前文传送门: Spring源码分析之预启动流程 Spring源码分析之BeanFactory体系结构 Spring源码分析之BeanFactoryPostProcessor调用过程详解 本文内容: 在 ...

  6. 【Spring源码分析】原型Bean实例化过程、byName与byType及FactoryBean获取Bean源码实现

    原型Bean加载过程 之前的文章,分析了非懒加载的单例Bean整个加载过程,除了非懒加载的单例Bean之外,Spring中还有一种Bean就是原型(Prototype)的Bean,看一下定义方式: & ...

  7. springMVC源码分析--HandlerInterceptor拦截器调用过程(二)

    在上一篇博客springMVC源码分析--HandlerInterceptor拦截器(一)中我们介绍了HandlerInterceptor拦截器相关的内容,了解到了HandlerInterceptor ...

  8. 从壹开始微服务 [ DDD ] 之十一 ║ 基于源码分析,命令分发的过程(二)

    缘起 哈喽小伙伴周三好,老张又来啦,DDD领域驱动设计的第二个D也快说完了,下一个系列我也在考虑之中,是 Id4 还是 Dockers 还没有想好,甚至昨天我还想,下一步是不是可以写一个简单的Angu ...

  9. Dubbo源码分析之ExtensionLoader加载过程解析

    ExtensionLoader加载机制阅读: Dubbo的类加载机制是模仿jdk的spi加载机制:  Jdk的SPI扩展加载机制:约定是当服务的提供者每增加一个接口的实现类时,需要在jar包的META ...

随机推荐

  1. SQL Server里ORDER BY的歧义性

    在今天的文章里,我想谈下SQL Server里非常有争议和复杂的话题:ORDER BY子句的歧义性. 视图与ORDER BY 我们用一个非常简单的SELECT语句开始. -- A very simpl ...

  2. linux常见进程与内核线程

    发现大量jdb2进程占用io资源.jdb2进程是一个文件系统的写journal的进程 kthreadd:这种内核线程只有一个,它的作用是管理调度其它的内核线程.它在内核初始化的时候被创建,会循环运行一 ...

  3. JS魔法堂:那些困扰你的DOM集合类型

    一.前言 大家先看看下面的js,猜猜结果会怎样吧! 可选答案: ①. 获取id属性值为id的节点元素 ②. 抛namedItem is undefined的异常 var nodes = documen ...

  4. [操作系统实验lab2]实验报告

    static void * alloc(u_int n, u_int align, int clear) { extern char end[]; int i; u_long alloced_mem; ...

  5. Chrome弹窗的简单应用(选择结构与循环结构)

    ★选择结构★ ★JS实现弹窗显示随机数 示例代码效果图   ★ 弹窗实现对随机数的进一步判断 示例代码效果图 ★综合应用   比较大小 ★ 判断成绩等级 ): : : : : alert(" ...

  6. Java编码规范

    1. Java命名约定 除了以下几个特例之外,命名时应始终采用完整的英文描述符.此外,一般应采用小写字母,但类名.接口名以及任何非初始单词的第一个字母要大写.1.1 一般概念 n 尽量使用完整 ...

  7. IIS理解

    WEB开发基础 1IIS原理 IIS的本质其实就是一个sorket的服务器,浏览器就是一个sorket的客户端,浏览器发送请求信息给IIS,IIS返回信息给浏览器显示,就这么简单. 1http.sys ...

  8. 后缀数组---New Distinct Substrings

    Description Given a string, we need to find the total number of its distinct substrings. Input T- nu ...

  9. 回文串---Hotaru's problem

    HDU   5371 Description Hotaru Ichijou recently is addicated to math problems. Now she is playing wit ...

  10. java jdk environment variables

    1. create system variable 2. edit the system path note: ;%JAVA_HOME%\bin;%JAVA_HOME%\jre\bin; 3. cre ...