HBase源码分析之MemStore的flush发起时机、判断条件等详情(二)

        在《HBase源码分析之MemStore的flush发起时机、判断条件等详情》一文中,我们详细介绍了MemStore flush的发起时机、判断条件等详情,主要是两类操作,一是会引起MemStore数据大小变化的Put、Delete、Append、Increment等操作,二是会引起HRegion变化的诸如Regin的分裂、合并以及做快照时的复制拷贝等,同样会触发MemStore的flush流程。同时,在《HBase源码分析之compact请求发起时机、判断条件等详情(一)》一文中,我们讲到了针对compact,在HRegionServer内部存在一个工作线程compactionChecker,它会周期性的工作,以检查是否达到能够发起compact请求的条件。那么,回过头来,我们再看MemStore
flush,它是不是也存在一个后台工作线程,能够周期性的工作,以检查是否达到能够发起flush请求的条件呢?本文,我们就之前《HBase源码分析之MemStore的flush发起时机、判断条件等详情》一文,做一个关于MemStore flush后台检查线程等内容的补充。

        在HRegionServer中,有一个和合并检查线程compactionChecker一样的Chore--periodicFlusher,它也是类似于compactionChecker的后台工作线程,它负责周期性的检查MemStore,查看是否达到发起MemStore flush的条件。其定义如下:

  /*
   * Check for flushes
   * 检查刷新请求
   */
  Chore periodicFlusher;

        它也是一个继承自Chore的工作线程,关于Chore的介绍,在《HBase源码分析之compact请求发起时机、判断条件等详情(一)》一文中我已经讲过了,这里不再做介绍。而periodicFlusher的初始化,自然同compactionChecker一样,也是在HRegionServer的initializeThreads()方法中完成的,代码如下:

    this.periodicFlusher = new PeriodicMemstoreFlusher(this.threadWakeFrequency, this);

        很简单,构造一个PeriodicMemstoreFlusher对象,而且其工作频率也是通过HRegionServer的threadWakeFrequency决定的。那么这个periodicFlusher到底是什么样的实现类,其工作原理是什么样子的呢?莫慌,让我为大家一一道来。

        首先看下PeriodicMemstoreFlusher的定义、成员变量与构造方法,代码如下:

  static class PeriodicMemstoreFlusher extends Chore {
    final HRegionServer server;
    final static int RANGE_OF_DELAY = 20000; //millisec
    final static int MIN_DELAY_TIME = 3000; //millisec
    public PeriodicMemstoreFlusher(int cacheFlushInterval, final HRegionServer server) {
      // cacheFlushInterval为flush的时间间隔
      super(server.getServerName() + "-MemstoreFlusherChore", cacheFlushInterval, server);
      this.server = server;
    }
  }

        通过成员变量和构造方法,我们可以看到,比较重要的就是线程中HRegionServer的实例server以及线程工作频率。另外它还提供了两个定值MIN_DELAY_TIME与RANGE_OF_DELAY,有什么用呢。继续看它的chore()方法:

    @Override
    protected void chore() {
      // 循环HRegionSever上的onlineRegions
      for (HRegion r : this.server.onlineRegions.values()) {

    	// HRegion为null的话直接跳过
    	if (r == null)
          continue;

    	// 调用HRegion上的shouldFlush()方法,判断是否可以进行flush
        if (r.shouldFlush()) {
          // 获取RegionServer上的MemStoreFlusher类型的memstore内存刷新管理对象
          FlushRequester requester = server.getFlushRequester();
          if (requester != null) {
        	// 随机延迟时间:20s内的一个随机时间+3s的基础时间
            long randomDelay = RandomUtils.nextInt(RANGE_OF_DELAY) + MIN_DELAY_TIME;
            LOG.info(getName() + " requesting flush for region " + r.getRegionNameAsString() +
                " after a delay of " + randomDelay);
            //Throttle the flushes by putting a delay. If we don't throttle, and there
            //is a balanced write-load on the regions in a table, we might end up
            //overwhelming the filesystem with too many flushes at once.
            // 通过设置一个延迟时间控制flush,防止Region上多个flush同一时间并发进行
            requester.requestDelayedFlush(r, randomDelay);
          }
        }
      }
    }
  }

        通过chore()方法我们知道,periodicFlusher线程周期性的对HRegionServer上所有在线Region进行检测,调用其shouldFlush()方法进行检测,如果该Region需要flush memstore,获取RegionServer上的MemStoreFlusher类型的memstore内存刷新管理对象,发起flush请求。

        需要注意的是,该flush请求携带一个固定加随机的延迟时间,其算法为:

long randomDelay = RandomUtils.nextInt(RANGE_OF_DELAY) + MIN_DELAY_TIME;

        MIN_DELAY_TIME就是我们上面提到的固定值3秒,然后再加上一个20s内的一个随机数。为什么要这么做呢?试想下,如果立即提交一个flush请求,或者在3秒后立即提交一个flush请求,是不是很容易就产生一个风暴,引起系统性能瓶颈呢?

        关于如何提交一个flush请求,前面的文章已经介绍过了,不再赘述。这里我们介绍下HRegion的shouldFlush()方法,代码如下:

  /**
   * Should the memstore be flushed now
   * memstore现在是否应该被flush
   */
  boolean shouldFlush() {
    // This is a rough measure.
	// 这里是一个粗略的测量
	// 上次flush之后,sequenceId的增长超过flushPerChanges,即发起一次flush
	// 次数限制通过参数hbase.regionserver.flush.per.changes配置,默认为30000000(3千万)
	// 也就是该Region上数据的改动次数,无论增、改、删等,超过一定的次数,即发起一次flush
	// 意味着会兼顾HRegion上的写请求及时flush到磁盘上
    if (this.lastFlushSeqId > 0
          && (this.lastFlushSeqId + this.flushPerChanges < this.sequenceId.get())) {
      return true;
    }

    // hbase.regionserver.optionalcacheflushinterval参数小于等于0,不会触发flush
    //
    if (flushCheckInterval <= 0) { //disabled
      return false;
    }
    long now = EnvironmentEdgeManager.currentTime();
    //if we flushed in the recent past, we don't need to do again now
    // 时间间隔未超过hbase.regionserver.optionalcacheflushinterval配置的时间间隔
    // 默认为3600000ms,即1小时
    if ((now - getLastFlushTime() < flushCheckInterval)) {
      return false;
    }

    //since we didn't flush in the recent past, flush now if certain conditions
    //are met. Return true on first such memstore hit.
    // 检测每个列簇,当其中一个列簇超过flushCheckInterval没有flush时,发起flush
    for (Store s : this.getStores().values()) {
      if (s.timeOfOldestEdit() < now - flushCheckInterval) {
        // we have an old enough edit in the memstore, flush
        return true;
      }
    }
    return false;
  }

        判断的逻辑比较清晰,概括如下:

        1、首先,上次flush之后,sequenceId的增长超过flushPerChanges,即发起一次flush:

              次数限制flushPerChanges是通过参数hbase.regionserver.flush.per.changes配置,默认为30000000(3千万),这个sequenceId的增长该Region上数据的改动次数,无论增、删、改或者append、increment等,它是对HRegion数据变动的一个考虑,即便是MemStore不大,数据变动的频繁了,也需要进行flush,以降低宕机后拆分日志的工作量;

        2、再看参数hbase.regionserver.optionalcacheflushinterval:

              参数小于等于0,不会触发flush,时间间隔未超过参数l配置的时间间隔的话,也不会触发flush。这个参数默认为3600000ms,即1小时;

        3、当超过参数配置的时间间隔,再检测每个列簇,当其中一个列簇超过flushCheckInterval没有flush时,发起flush,也就是说它有足够久的数据没有被flush。

        以上就是HRegionServer内部PeriodicMemstoreFlusher工作线程periodicFlusher的全部内容。同时,在上面针对每个HRegion的循环,以及后面针对每个HStore的判断,我们可以发现,flush还是以Region为最小单位进行的。即便是某个列簇下MemStore过大或者过旧,另外一个MemStore还比较小或者比较新的话,它还是跟着那个过大或者过旧的列簇一起flush,这也是HBase饱受诟病的列簇不能过多的原因之一。在HBase1.1.2版本中,有对于MemStore
flush的改进,改成了以HStore,即列簇为单位进行。此乃后话,我们以后再做分析。

时间: 2025-01-20 18:39:51

HBase源码分析之MemStore的flush发起时机、判断条件等详情(二)的相关文章

HBase源码分析之MemStore的flush发起时机、判断条件等详情

        前面的几篇文章,我们详细介绍了HBase中HRegion上MemStore的flsuh流程,以及HRegionServer上MemStore的flush处理流程.那么,flush到底是在什么情况下触发的呢?本文我们将详细探究下HBase中MemStore的flush流程的发起时机,看看到底都有哪些操作,或者哪些后台服务进程会触发MemStore的flush.         首先,在<HBase源码分析之HRegionServer上MemStore的flush处理流程(一)>和

HBase源码分析之HRegionServer上MemStore的flush处理流程(一)

        在<HBase源码分析之HRegion上MemStore的flsuh流程(一)>.<HBase源码分析之HRegion上MemStore的flsuh流程(二)>等文中,我们介绍了HRegion上Memstore flush的主体流程和主要细节.但是,HRegion只是HBase表中按照行的方向对一片连续的数据区域的抽象,它并不能对外提供单独的服务,供客户端或者HBase其它实体调用.而HRegion上MemStore的flush还是要通过HRegionServer来

HBase源码分析之HRegionServer上MemStore的flush处理流程(二)

        继上篇文章<HBase源码分析之HRegionServer上MemStore的flush处理流程(一)>遗留的问题之后,本文我们接着研究HRegionServer上MemStore的flush处理流程,重点讲述下如何选择一个HRegion进行flush以缓解MemStore压力,还有HRegion的flush是如何发起的.         我们先来看下第一个问题:如何选择一个HRegion进行flush以缓解MemStore压力.上文中我们讲到过flush处理线程如果从flus

HBase源码分析之HRegion上MemStore的flsuh流程(二)

        继上篇<HBase源码分析之HRegion上MemStore的flsuh流程(一)>之后,我们继续分析下HRegion上MemStore flush的核心方法internalFlushcache(),它的主要流程如图所示:         其中,internalFlushcache()方法的代码如下: /** * Flush the memstore. Flushing the memstore is a little tricky. We have a lot of upda

HBase源码分析之compact请求发起时机、判断条件等详情(一)

        一般说来,任何一个比较复杂的分布式系统,针对能够使得其性能得到大幅提升的某一内部处理流程,必然有一个定期检查机制,使得该流程在满足一定条件的情况下,能够自发的进行,这样才能够很好的体现出复杂系统的自我适应与自我调节能力.我们知道,HBase内部的compact处理流程是为了解决MemStore Flush之后,文件数目太多,导致读数据性能大大下降的一种自我调节手段,它会将文件按照某种策略进行合并,大大提升HBase的数据读性能.那么,基于我刚才的陈述,compact流程是否有一个

HBase源码分析之HRegion上compact流程分析(三)

        在<HBase源码分析之HRegion上compact流程分析(二)>一文中,我们没有讲解真正执行合并的CompactionContext的compact()方法.现在我们来分析下它的具体实现.         首先,CompactionContext表示合并的上下文信息,它只是一个抽象类,其compact()并没有实现,代码如下: /** * Runs the compaction based on current selection. select/forceSelect

HBase源码分析之HRegion上MemStore的flsuh流程(一)

        了解HBase架构的用户应该知道,HBase是一种基于LSM模型的分布式数据库.LSM的全称是Log-Structured Merge-Trees,即日志-结构化合并-树.相比于Oracle普通索引所采用的B+树,LSM模型的最大特点就是,在读写之间采取一种平衡,牺牲部分读数据的性能,来大幅度的提升写数据的性能.通俗的讲,HBase写数据如此快,正是由于基于LSM模型,将数据写入内存和日志文件后即立即返回.         但是,数据始终在内存和日志中是不妥当的,首先内存毕竟是有

写在HBase源码分析前面的话

       本版块全部为HBase1.0.2相关源码分析文章,系个人研究源码原创写成,除对部分引用标示外,其余均为原创,或翻译源码注释.        该系列文章为与网友交流HBase学习,不做任何其他商业用途~~O(∩_∩)O哈哈~         由于水平有限,文章基本上是边读源码,边翻译注释,边分析源码写成,没有较强的前后逻辑性,我会在写完全部文章后再回头整理.         由于水平有限,文章中可能存在理解错误的地方,欢迎各位针对文章中的错误.问题或者其他指导建议,踊跃评论,多多指点

HBase源码分析:HTable put过程

HBase版本:0.94.15-cdh4.7.0 在 HBase中,大部分的操作都是在RegionServer完成的,Client端想要插入.删除.查询数据都需要先找到相应的 RegionServer.什么叫相应的RegionServer?就是管理你要操作的那个Region的RegionServer.Client本身并 不知道哪个RegionServer管理哪个Region,那么它是如何找到相应的RegionServer的?本文就是在研究源码的基础上了解这个过程. 首先来看看写过程的序列图: 客