在HRegionServer中,有一个和合并检查线程compactionChecker一样的Chore--periodicFlusher,它也是类似于compactionChecker的后台工作线程,它负责周期性的检查MemStore,查看是否达到发起MemStore flush的条件。其定义如下:
很简单,构造一个PeriodicMemstoreFlusher对象,而且其工作频率也是通过HRegionServer的threadWakeFrequency决定的。那么这个periodicFlusher到底是什么样的实现类,其工作原理是什么样子的呢?莫慌,让我为大家一一道来。
首先看下PeriodicMemstoreFlusher的定义、成员变量与构造方法,代码如下:
通过成员变量和构造方法,我们可以看到,比较重要的就是线程中HRegionServer的实例server以及线程工作频率。另外它还提供了两个定值MIN_DELAY_TIME与RANGE_OF_DELAY,有什么用呢。继续看它的chore()方法:
通过chore()方法我们知道,periodicFlusher线程周期性的对HRegionServer上所有在线Region进行检测,调用其shouldFlush()方法进行检测,如果该Region需要flush memstore,获取RegionServer上的MemStoreFlusher类型的memstore内存刷新管理对象,发起flush请求。
需要注意的是,该flush请求携带一个固定加随机的延迟时间,其算法为:
MIN_DELAY_TIME就是我们上面提到的固定值3秒,然后再加上一个20s内的一个随机数。为什么要这么做呢?试想下,如果立即提交一个flush请求,或者在3秒后立即提交一个flush请求,是不是很容易就产生一个风暴,引起系统性能瓶颈呢?
关于如何提交一个flush请求,前面的文章已经介绍过了,不再赘述。这里我们介绍下HRegion的shouldFlush()方法,代码如下:
判断的逻辑比较清晰,概括如下:
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,即列簇为单位进行。此乃后话,我们以后再做分析。