Hbase源码分析(十)MemStore的flush处理(上)2021SC@SDUSC


前言

本文介绍了HRegion上Memstore flush的主体流程和主要细节。但是,HRegion只是HBase表中按照行的方向对一片连续的数据区域的抽象,它并不能对外提供单独的服务,供客户端或者HBase其它实体调用。而HRegion上MemStore的flush还是要通过HRegionServer来对外提供服务的。


如何初始化cacheFlusher

一.cacheFlusher

在HRegionServer中,有一个叫做cacheFlusher的东西,它是什么呢?我们先看一下它是如何被定义的:

// Cache flushing
  // memstore内存刷新管理对象
  protected MemStoreFlusher cacheFlusher;

发现,cacheFlusher是MemStoreFlusher类型的一个对象,我们来看下类的注释及定义:

/**
 * Thread that flushes cache on request
 * 处理刷新缓存请求的线程
 *
 * NOTE: This class extends Thread rather than Chore because the sleep time
 * can be interrupted when there is something to do, rather than the Chore
 * sleep time which is invariant.
 *
 * @see FlushRequester
 */
@InterfaceAudience.Private
class MemStoreFlusher implements FlushRequester {

cacheFlusher实际上就是HRegionServer上处理刷新缓存请求的线程。那么接下来的问题就是,cacheFlusher是如何被初始化的?它又是如何处理flush请求的?

首先,我们发现HRegionServer继承自HasThread,而HasThread实现了Runnable接口,那么在其内部肯定会执行run()方法,而run()方法的开始,有如下代码:

 try {
      // Do pre-registration initializations; zookeeper, lease threads, etc.
      preRegistrationInitialization();
    } catch (Throwable e) {
      abort("Fatal exception during initialization", e);
    }

继续往下看preRegistrationInitialization()方法,在其内部,调用了initializeThreads()方法,如下:

if (!isStopped() && !isAborted()) {
        initializeThreads();
      }

而这个initializeThreads()方法,做的主要工作就是初始化HRegionServer内部的各种工作线程,其中就包括cacheFlusher,代码如下:

// Cache flushing thread.
	// 缓存刷新线程
    this.cacheFlusher = new MemStoreFlusher(conf, this);
    

二.MemStoreFlusher类最主要的几个成员变量

然后我们在看这个MemStoreFlusher类是如何定义及工作的。首先看下它最主要的几个成员变量:

flushQueue

    首先便是flushQueue,其定义如下:
private final BlockingQueue<FlushQueueEntry> flushQueue =
    new DelayQueue<FlushQueueEntry>();

flushQueue是MemStoreFlusher中非常重要的一个变量,它是一个存储了Region刷新缓存请求的队列。而与flushQueue同时被更新的是regionsInQueue,它存储的是HRegion到FlushRegionEntry映射关系的集合,FlushRegionEntry是对发起memstore刷新请求的HRegion的一个封装,不仅包含了HRegion实例,还包括HRegion刷新memstore请求的产生时间,到期时间,以及一种类似续约的处理方式,即延长该请求的到期时间等。regionsInQueue的定义如下:

private final Map<HRegion, FlushRegionEntry> regionsInQueue =
    new HashMap<HRegion, FlushRegionEntry>();

flushQueue和regionsInQueue的更新是同步的,即如果在flushQueue中加入或删除一条记录,那么在regionsInQueue中也会同步加入或删除一条记录。

flushHandlers

接下来便是flushHandlers,它是FlushHandler类型的一个数组,定义如下:

private final FlushHandler[] flushHandlers;

FlushHandler是处理缓存刷新的线程类,线程一旦启动后,在其run()方法内,就会不停的从flushQueue队列中拉取flush请求进行处理,其类的定义如下:

private class FlushHandler extends HasThread {

三.MemStoreFlusher的构造及成员变量的初始化

下面,我们来看下MemStoreFlusher的构造及成员变量的初始化,构造函数如下:

 public MemStoreFlusher(final Configuration conf,
      final HRegionServer server) {
    super();
    
    // 赋值RegionServer实例server
    this.server = server;
    
    // 线程唤醒频率threadWakeFrequency,取参数hbase.server.thread.wakefrequency配置的值,默认为10s,即线程的工作频率
    this.threadWakeFrequency =
      conf.getLong(HConstants.THREAD_WAKE_FREQUENCY, 10 * 1000);
    
    // 获取最大可用堆内存max
    long max = ManagementFactory.getMemoryMXBean().getHeapMemoryUsage().getMax();
    
    // 获取全局memstore所占堆内存的百分比globalMemStorePercent
    float globalMemStorePercent = HeapMemorySizeUtil.getGlobalMemStorePercent(conf, true);
    
    // 获取全局memstore大小限制值globalMemStoreLimit
    this.globalMemStoreLimit = (long) (max * globalMemStorePercent);
    
    // 获取全局memstore大小限制值的低水平线百分比globalMemStoreLimitLowMarkPercent
    this.globalMemStoreLimitLowMarkPercent = 
        HeapMemorySizeUtil.getGlobalMemStoreLowerMark(conf, globalMemStorePercent);
    
    // 获取全局memstore大小限制值的低水平线globalMemStoreLimitLowMark
    this.globalMemStoreLimitLowMark = 
        (long) (this.globalMemStoreLimit * this.globalMemStoreLimitLowMarkPercent);
 
    // 获取阻塞等待时间blockingWaitTime,取参数hbase.hstore.blockingWaitTime,默认为90000
    this.blockingWaitTime = conf.getInt("hbase.hstore.blockingWaitTime",
      90000);
    
    // 获取flush处理线程数目handlerCount,取参数hbase.hstore.flusher.count,默认为2
    int handlerCount = conf.getInt("hbase.hstore.flusher.count", 2);
    
    // 构造handlerCount个flush处理线程数组,默认为2个,可通过hbase.hstore.flusher.count设置
    this.flushHandlers = new FlushHandler[handlerCount];
    
    // 记录日志信息
    LOG.info("globalMemStoreLimit=" +
      StringUtils.humanReadableInt(this.globalMemStoreLimit) +
      ", globalMemStoreLimitLowMark=" +
      StringUtils.humanReadableInt(this.globalMemStoreLimitLowMark) +
      ", maxHeap=" + StringUtils.humanReadableInt(max));
  }

MemStoreFlusher的构造函数比较简单,我们重点分析下获取全局memstore所占堆内存的百分比globalMemStorePercent的HeapMemorySizeUtil类的getGlobalMemStorePercent()方法,和获取全局memstore大小限制值的低水平线百分比globalMemStoreLimitLowMarkPercent的HeapMemorySizeUtil类的getGlobalMemStoreLowerMark()方法。
首先,看下获取全局memstore所占堆内存的百分比globalMemStorePercent的HeapMemorySizeUtil类的getGlobalMemStorePercent()方法,代码如下:

public static float getGlobalMemStorePercent(final Configuration c, final boolean logInvalid) {
    
	// 获取全局memstore的大小,优先取参数hbase.regionserver.global.memstore.size,
	// 未配置的话再取参数hbase.regionserver.global.memstore.upperLimit,
	// 如果还未配置的话,默认为0.4
	float limit = c.getFloat(MEMSTORE_SIZE_KEY,
        c.getFloat(MEMSTORE_SIZE_OLD_KEY, DEFAULT_MEMSTORE_SIZE));
    
	// 如果limit的值在区间(0,0.8]之外的话
	if (limit > 0.8f || limit <= 0.0f) {
      if (logInvalid) {// 根据参数logInvalid确定是否记录警告日志
        LOG.warn("Setting global memstore limit to default of " + DEFAULT_MEMSTORE_SIZE
            + " because supplied value outside allowed range of (0 -> 0.8]");
      }
      
      // 将limit设置为0.4
      limit = DEFAULT_MEMSTORE_SIZE;
    }
	
	// 返回limit
    return limit;
  }

这个方法的主要作用就是获取配置的全局memstore占整个heap内存的百分比。获取的逻辑如下:
1、获取配置的全局memstore占整个heap内存的百分比limit:优先取参数hbase.regionserver.global.memstore.size,未配置的话再取参数hbase.regionserver.global.memstore.upperLimit,如果还未配置的话,默认为0.4;

2、判断limit是否在区间(0,0.8]之外,根据参数logInvalid确定是否记录警告日志,并将limit设置为默认值0.4;

3、返回limit。

下面,我们再看下获取全局memstore大小限制值的低水平线百分比globalMemStoreLimitLowMarkPercent的HeapMemorySizeUtil类的getGlobalMemStoreLowerMark()方法,代码如下:

public static float getGlobalMemStoreLowerMark(final Configuration c, float globalMemStorePercent) {
    
	// 取新参数hbase.regionserver.global.memstore.size.lower.limit  
	String lowMarkPercentStr = c.get(MEMSTORE_SIZE_LOWER_LIMIT_KEY);
	
	// 如果新参数配置了的话,直接转化为double并返回
    if (lowMarkPercentStr != null) {
      return Float.parseFloat(lowMarkPercentStr);
    }
    
    // 取旧参数hbase.regionserver.global.memstore.lowerLimit"
    String lowerWaterMarkOldValStr = c.get(MEMSTORE_SIZE_LOWER_LIMIT_OLD_KEY);
    
    // 如果旧参数配置的话,记录警告日志信息
    if (lowerWaterMarkOldValStr != null) {
      LOG.warn(MEMSTORE_SIZE_LOWER_LIMIT_OLD_KEY + " is deprecated. Instead use "
          + MEMSTORE_SIZE_LOWER_LIMIT_KEY);
      
      // 转化为double类型lowerWaterMarkOldVal
      float lowerWaterMarkOldVal = Float.parseFloat(lowerWaterMarkOldValStr);
      
      // 如果参数值大于计算得到的全局memstore所占堆内存的百分比,赋值为globalMemStorePercent,并记录日志信息
      if (lowerWaterMarkOldVal > globalMemStorePercent) {
        lowerWaterMarkOldVal = globalMemStorePercent;
        LOG.info("Setting globalMemStoreLimitLowMark == globalMemStoreLimit " + "because supplied "
            + MEMSTORE_SIZE_LOWER_LIMIT_OLD_KEY + " was > " + MEMSTORE_SIZE_OLD_KEY);
      }
      
      // 返回lowerWaterMarkOldVal / globalMemStorePercent
      return lowerWaterMarkOldVal / globalMemStorePercent;
    }
    
    // 如果新旧参数均未配置的话,默认为0.95
    return DEFAULT_MEMSTORE_SIZE_LOWER_LIMIT;
  }

这个方法的主要作用就是获取配置的全局memstore内存占全部heap内存的低水平线百分比。获取的逻辑如下:
1、取新参数hbase.regionserver.global.memstore.size.lower.limit配置的值,如果新参数配置了的话,直接转化为double并返回;

2、如果新参数未配置的话,取旧参数hbase.regionserver.global.memstore.lowerLimit配置的值,如果旧参数配置的话,记录警告日志信息,并:

2.1、将旧参数配置的值转化为double类型lowerWaterMarkOldVal;

2.2、如果旧参数值大于计算得到的全局memstore所占堆内存的百分比,赋值为globalMemStorePercent,并记录日志信息;

2.3、返回lowerWaterMarkOldVal / globalMemStorePercent;

3、如果新旧参数均未配置的话,默认为0.95。

总结

以上就是今天要讲的内容,本文简单介绍了如何初始化cacheFlusher,而如何处理flush请求的将在下文揭晓。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值