《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);

非常easy,构造一个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请求,是不是非常easy就产生一个风暴。引起系统性能瓶颈呢?

关于怎样提交一个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,即列簇为单位进行。

此乃后话,我们以后再做分析。

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

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

    前面的几篇文章.我们具体介绍了HBase中HRegion上MemStore的flsuh流程,以及HRegionServer上MemStore的flush处理流程.那么,flush究竟是在什么情况下触发 ...

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

    在<HBase源代码分析之HRegion上MemStore的flsuh流程(一)>.<HBase源代码分析之HRegion上MemStore的flsuh流程(二)>等文中.我们 ...

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

    继上篇文章<HBase源代码分析之HRegionServer上MemStore的flush处理流程(一)>遗留的问题之后,本文我们接着研究HRegionServer上MemStore的fl ...

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

    继上篇<HBase源代码分析之HRegion上MemStore的flsuh流程(一)>之后.我们继续分析下HRegion上MemStore flush的核心方法internalFlushc ...

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

    了解HBase架构的用户应该知道,HBase是一种基于LSM模型的分布式数据库.LSM的全称是Log-Structured Merge-Trees.即日志-结构化合并-树. 相比于Oracle普通索引 ...

  6. HBase源代码分析

    http://www.docin.com/p-647062205.html http://blog.csdn.net/luyee2010/article/category/1285622 http:/ ...

  7. Hadoop源代码分析

    http://wenku.baidu.com/link?url=R-QoZXhc918qoO0BX6eXI9_uPU75whF62vFFUBIR-7c5XAYUVxDRX5Rs6QZR9hrBnUdM ...

  8. Hadoop源代码分析(完整版)

    Hadoop源代码分析(一) 关键字: 分布式云计算 Google的核心竞争技术是它的计算平台.Google的大牛们用了下面5篇文章,介绍了它们的计算设施. GoogleCluster:http:// ...

  9. 1、Hbase原理分析

    一.Hbase介绍 1.1.对Hbase的认识 HBase作为面向列的数据库运行在HDFS之上,HDFS缺乏随机读写操作,HBase正是为此而出现. HBase参考 Google 的 Bigtable ...

随机推荐

  1. eclipse怎么关闭spring dashboard

    进入help-install new software-what is already installed?-卸载spring board

  2. activemq修改admin密码

    vim /usr/local/activemq/conf/jetty-realm.properties 用户名:密码,角色 mq: 123, admin 那么打开管理后台,用户是mq,密码是123,具 ...

  3. 将双击“root的主文件”弹出的窗口设置为文件浏览器

    1.双击桌面"root的文件夹"图标, 在过去Centos版本之前,每次双击“root主文件夹”都会弹出文件管理窗口: 解决办法:         关闭所有窗口后,重新双击图标: ...

  4. 9、Django实战第9天:用户注册功能

    今天完成的是用户注册功能... 首先把注册页面的前端文件register.html复制到templates目录下 编辑users.views.py,创建一个注册的类 class RegisterVie ...

  5. centos 7下查找大文件、大目录和常见文件查找操作

    根据园子 潇湘隐者的文章 <Linux如何查找大文件或目录总结>结合实际运维需要整理出常用命令 目标文件和目录查找主要使用 find 命令 结合 xargs (给命令传递参数的一个过滤器, ...

  6. [BZOJ5462][APIO2018]新家(线段树+堆)

    其实这个题第一反应一定是线段树分治,但是这样反而更难考虑了(实际上是可做的但很难想到),可见即使看上去最贴切的算法也未必能有效果. 考虑这个DS题,没有什么模型的转化,可能用到的无非就是线段树.平衡树 ...

  7. 【计算几何】【辛普森积分法】UVALive - 7076 - Highway

    春节前后想了好久才在队友的讲解下想明白…… 太难讲了,我就不讲了,大概就是考虑直着走到高速上还是斜着走到高速上,然后平移直线和大圆相切,把生成的最大的“桥”和大圆并一下就行了. #include< ...

  8. 【最短路】【最大流】bzoj3931 [CQOI2015]网络吞吐量

    跑出最短路图,然后把结点拆点跑最大流. #include<cstdio> #include<queue> #include<cstring> #include< ...

  9. 【枚举】bzoj1709 [Usaco2007 Oct]Super Paintball超级弹珠

    由于子弹的轨迹是可逆的,因此我们可以枚举所有敌人的位置,然后统计他们能打到的位置,这些位置也就是能打到他们的位置咯. O(n*k). #include<cstdio> using name ...

  10. 【kmp算法】模板

    void GetFail(char P[],int __next[])//__next[i]表示s[0]~s[i-1]的前缀中,最大长度相等的前后缀是多少 { __next[0]=-1; int le ...