hbase源码之 compact源码(一)
hbase compact流程较多,这里分章节介绍,首先介绍compact在regionserver中的调用流程,并不会涉及真正的compact读取合并文件的流程,后续介绍。
在regionserver启动时,会初始化compactsplitthread以及CompactionChecker。
/*
* Check for compactions requests.
* 检查合并请求
*/
ScheduledChore compactionChecker; // Compactions
public CompactSplitThread compactSplitThread;
compactionChecker是ScheduledChore类型,而ScheduledChore是hbase定期执行的一个task,如下所示,由注释可知,是hbase周期性执行的一个task。在Regionserver中可以看到flushChecker成员变量也是ScheduledChore类型的。ScheduledChore继承自Runable,因此是一个线程,主要逻辑在其run方法中。
/**
* ScheduledChore is a task performed on a period in hbase. ScheduledChores become active once
* scheduled with a {@link ChoreService} via {@link ChoreService#scheduleChore(ScheduledChore)}. The
* chore is run in a {@link ScheduledThreadPoolExecutor} and competes with other ScheduledChores for
* access to the threads in the core thread pool. If an unhandled exception occurs, the chore
* cancellation is logged. Implementers should consider whether or not the Chore will be able to
* execute within the defined period. It is bad practice to define a ScheduledChore whose execution
* time exceeds its period since it will try to hog one of the threads in the {@link ChoreService}'s
* thread pool.
* <p>
* Don't subclass ScheduledChore if the task relies on being woken up for something to do, such as
* an entry being added to a queue, etc.
*/
//scheduledChore继承自Runnable 所以Chore是一个线程
//1. 是hbase定期执行的一个task, 2.在它所在的线程内执行 3.提供了loop循环和sleep机制
@InterfaceAudience.Private
public abstract class ScheduledChore implements Runnable {
}
ScheduledChore中的比较重要和成员变量稍作说明,如下:
//睡眠周期
private final int period;
//上一次执行改task的时间
private long timeOfLastRun = -1;
//本次执行的时间
private long timeOfThisRun = -1;
//该ScheduledChore是否完成初始化,在第一次执行该check时会执行,调用的是initChore()方法,该方法直接返回true,不做任何逻辑处理。
private boolean initialChoreComplete = false
其中还有一个重要的成员变量stopper,stopper是实现了Stopper接口的任意一个对象。根据注释可知,stopper是停止ScheduledChore的一种方式,一旦chore察觉到已经stopped了,会cancel它自己。在Regionserver初始化实例化compactionChecker的时候,会将该stopper设置为this,因此,此处以为这当RS stop时,该chore会感知到,自动cancel其compact。具体的代码:
在ScheduledChore中
/**
* A means by which a ScheduledChore can be stopped. Once a chore recognizes that it has been
* stopped, it will cancel itself. This is particularly useful in the case where a single stopper
* instance is given to multiple chores. In such a case, a single {@link Stoppable#stop(String)}
* command can cause many chores to stop together.
*/
private final Stoppable stopper; 在RegionServer中
this.compactionChecker = new CompactionChecker(this,this.frequency, stopper: this)
ScheduledChore中最核心的部分,即其run方法,run()方法通过一系列的判断 然后周期性执行chore()方法。下面我们一行行解释。
public void run() {
//将timeOfLastRun设置为当前timeOfThisRun,同时将timeOfThisRun设置为当前时间
updateTimeTrackingBeforeRun();
if (missedStartTime() && isScheduled()) {
onChoreMissedStartTime();
if (LOG.isInfoEnabled()) LOG.info("Chore: " + getName() + " missed its start time");
} else if (stopper.isStopped() || !isScheduled()) {
cancel(false);
cleanup();
if (LOG.isInfoEnabled()) LOG.info("Chore: " + getName() + " was stopped");
} else {
try {
if (!initialChoreComplete) {
initialChoreComplete = initialChore();
} else {
chore();
}
} catch (Throwable t) {
if (LOG.isErrorEnabled()) LOG.error("Caught error", t);
if (this.stopper.isStopped()) {
cancel(false);
cleanup();
}
}
}
}
在run方法中,首先调用updateTimeTrackingBeforeRun()方法,该方法很简单,只是简单的update timeOfLastRun和timeOfthsiRun(这两个变量初始化为-1)。每次周期性执行时都会更新。
/**
* Update our time tracking members. Called at the start of an execution of this chore's run()
* method so that a correct decision can be made as to whether or not we missed the start time
*/
private synchronized void updateTimeTrackingBeforeRun() {
timeOfLastRun = timeOfThisRun;
timeOfThisRun = System.currentTimeMillis();
}
然后对时间进行判断missedStartTime() && isScheduled(),在compact中isScheduled返回fasle。跳到else if分支,当该chore所依托的载体(此处即为RS)stop了,该chore会自动退出。最终会进入
最后的else分支。在第一次运行时,initialChoreComplete是false,因此会执行initialChore方法,该方法直接返回true,不会做任何处理。
在一切都准备好后,会周期执行chore方法,在Regionserver中有CompactionChecker,继承自ScheduledChore, 实现了自己的chore方法,在该方法中会根据判断执行具体的requestCompact方法,下次介绍,
逻辑中也可以看到,首先是判断是否需要compact,如果需要则不会再判断是否需要majorcompact。如下:
/*
* Inner class that runs on a long period checking if regions need compaction.
*/
private static class CompactionChecker extends ScheduledChore {
private final HRegionServer instance;
private final int majorCompactPriority;
private final static int DEFAULT_PRIORITY = Integer.MAX_VALUE;
private long iteration = 0; CompactionChecker(final HRegionServer h, final int sleepTime,
final Stoppable stopper) {
//调用父类的构造方法
super("CompactionChecker", stopper, sleepTime);
//将载体h赋值给instance
this.instance = h;
LOG.info(this.getName() + " runs every " + StringUtils.formatTime(sleepTime)); /* MajorCompactPriority is configurable.
* If not set, the compaction will use default priority.
*/
//设置major合并优先级,取参数hbase.regionserver.compactionChecker.majorCompactPriority,默认为Integer.MAX_VALUE
this.majorCompactPriority = this.instance.conf.
getInt("hbase.regionserver.compactionChecker.majorCompactPriority",
DEFAULT_PRIORITY);
} //ScheduledChore的run方法会一直调用chore函数
@Override
protected void chore() {
//遍历instance下的所有online的region 进行循环检测
//onlineRegions是HRegionServer上存储的所有能够提供有效服务的在线Region集合;
for (HRegion r : this.instance.onlineRegions.values()) {
if (r == null)
continue;
//取出每个region的store
for (Store s : r.getStores().values()) {
try {
//检查是否需要compact的时间间隔,一般情况是在比如memstore flush后或者其他事件触发compact的,但是有时也需要不同的compact策略,
// 所以需要周期性的检查具体间隔=hbase.server.compactchecker.interval.multiplier * hbase.server.thread.wakefrequency,默认1000;
long multiplier = s.getCompactionCheckMultiplier();
assert multiplier > 0;
// 未到整数倍,跳过,每当迭代因子iteration为合并检查倍增器multiplier的整数倍时,才会发起检查
if (iteration % multiplier != 0) continue;
if (s.needsCompaction()) {//// 需要合并的话,发起SystemCompaction请求,此处最终比较的是是否当前hfile数量减去正在compacting的文件数大于设置的compact min
//值。若满足则执行systemcompact
// Queue a compaction. Will recognize if major is needed.
this.instance.compactSplitThread.requestSystemCompaction(r, s, getName()
+ " requests compaction");
} else if (s.isMajorCompaction()) {
if (majorCompactPriority == DEFAULT_PRIORITY
|| majorCompactPriority > r.getCompactPriority()) {
this.instance.compactSplitThread.requestCompaction(r, s, getName()
+ " requests major compaction; use default priority", null);
} else {
this.instance.compactSplitThread.requestCompaction(r, s, getName()
+ " requests major compaction; use configured priority",
this.majorCompactPriority, null);
}
}
} catch (IOException e) {
LOG.warn("Failed major compaction check on " + r, e);
}
}
}
iteration = (iteration == Long.MAX_VALUE) ? 0 : (iteration + 1);
}
}
其中判断是否需要compact比较简单,主要是isMajorCompaction的判断。最主要的逻辑如下:()
获取下一次majorcompact的时间mc
获取所有需要compact的file的modify time,已得到所有的file中最小的时间戳lowTimestamp,如果lowTimestamp<now - mc以为这需要进行major compact了。
如果此时只有一个file,则进行如下判断
如果未过期,且其block的本定性不要求满足,则进行majorcompact,否则不进行major compact
如果过期,则进行major compact
public boolean isMajorCompaction(final Collection<StoreFile> filesToCompact)
throws IOException {
boolean result = false;
//获取下一次major compact的时间
long mcTime = getNextMajorCompactTime(filesToCompact);
if (filesToCompact == null || filesToCompact.isEmpty() || mcTime == 0) {
return result;
}
// TODO: Use better method for determining stamp of last major (HBASE-2990)
//获取待合并文件中modify的最小时间戳 以及当前时间
long lowTimestamp = StoreUtils.getLowestTimestamp(filesToCompact);
long now = System.currentTimeMillis();
if (lowTimestamp > 0l && lowTimestamp < (now - mcTime)) {
//lowTimestamp < (now - mcTime)即意味着当前时间位于进行major compact的时间范围之内,要进行compact
// Major compaction time has elapsed.
long cfTtl = this.storeConfigInfo.getStoreFileTtl();
if (filesToCompact.size() == 1) {
// Single file
StoreFile sf = filesToCompact.iterator().next();
Long minTimestamp = sf.getMinimumTimestamp();
//文件存留时间oldest
long oldest = (minTimestamp == null)
? Long.MIN_VALUE
: now - minTimestamp.longValue();
if (sf.isMajorCompaction() &&
(cfTtl == HConstants.FOREVER || oldest < cfTtl)) {
float blockLocalityIndex = sf.getHDFSBlockDistribution().getBlockLocalityIndex(
RSRpcServices.getHostname(comConf.conf, false)
);
if (blockLocalityIndex < comConf.getMinLocalityToForceCompact()) {
if (LOG.isDebugEnabled()) {
LOG.debug("Major compaction triggered on only store " + this +
"; to make hdfs blocks local, current blockLocalityIndex is " +
blockLocalityIndex + " (min " + comConf.getMinLocalityToForceCompact() +
")");
}
result = true;
} else {
if (LOG.isDebugEnabled()) {
LOG.debug("Skipping major compaction of " + this +
" because one (major) compacted file only, oldestTime " +
oldest + "ms is < ttl=" + cfTtl + " and blockLocalityIndex is " +
blockLocalityIndex + " (min " + comConf.getMinLocalityToForceCompact() +
")");
}
}
} else if (cfTtl != HConstants.FOREVER && oldest > cfTtl) {////只有一个hfile(最早的ts>ttl)整个文件过期 => 进行marjor compact
LOG.debug("Major compaction triggered on store " + this +
", because keyvalues outdated; time since last major compaction " +
(now - lowTimestamp) + "ms");
result = true;
}
} else {
if (LOG.isDebugEnabled()) {
LOG.debug("Major compaction triggered on store " + this +
"; time since last major compaction " + (now - lowTimestamp) + "ms");
}
result = true;
}
}
return result;
}
hbase源码之 compact源码(一)的更多相关文章
- Hbase源码之 compact源码(二)
compact一中介绍了HBASE compact的调度流程,本篇文章主要介绍实际进行compact的过程.先从上文中的chore中接入,在HRegionserver中的compactChecker ...
- 【图解源码】Zookeeper3.7源码分析,包含服务启动流程源码、网络通信源码、RequestProcessor处理请求源码
Zookeeper3.7源码剖析 能力目标 能基于Maven导入最新版Zookeeper源码 能说出Zookeeper单机启动流程 理解Zookeeper默认通信中4个线程的作用 掌握Zookeepe ...
- spring源码解析——spring源码导入eclipse
一.前言 众所周知,spring的强大之处.几乎所有的企业级开发中,都使用了spring了.在日常的开发中,我们是否只知道spring的配置,以及简单的使用场景.对其实现的代码没有进行深入的了 ...
- [译] 给PHP开发者的PHP源码-第一部分-源码结构
文章来自:http://www.hoohack.me/2016/02/04/phps-source-code-for-php-developers-ch 原文:http://blog.ircmaxel ...
- ios源码-ios游戏源码-ios源码下载
游戏源码 一款休闲类的音乐小游戏源码 该源码实现了一款休闲类的音乐小游戏源码,该游戏的源码很简单,而且游戏的玩法也很容易学会,只要我们点击视图中的grid,就可以 人气:2943运行环境:/Xco ...
- 【转】编译Android系统源码和内核源码
原文网址:http://blog.csdn.net/jiangwei0910410003/article/details/37988637 好长时间没有写blog了,之所以没有写,主要还是工作上的事, ...
- 【转】Android手机客户端关于二维码扫描的源码--不错
原文网址:https://github.com/SkillCollege/QrCodeScan QrCodeScan 这是Android手机客户端关于二维码扫描的源码,使用了高效的ZBar解码库,并修 ...
- 【安卓本卓】Android系统源码篇之(一)源码获取、源码目录结构及源码阅读工具简介
前言 古人常说,“熟读唐诗三百首,不会作诗也会吟”,说明了大量阅读诗歌名篇对学习作诗有非常大的帮助.做开发也一样,Android源码是全世界最优秀的Android工程师编写的代码,也是A ...
- 追源索骥:透过源码看懂Flink核心框架的执行流程
li,ol.inline>li{display:inline-block;padding-right:5px;padding-left:5px}dl{margin-bottom:20px}dt, ...
随机推荐
- python3之int类的常用方法练习
int类的常用方法练习 #coding:utf-8 #int类的常用方法 num1 = 18 num2 = -15 #查询创建num1所用的类 print(type(num1)) #num1占用的最小 ...
- 妹纸对网易严选的Bra是什么评价?
声明:这是一篇超级严肃的技术文章,请本着学习交流的态度阅读,谢谢! 一.网易商品评论爬取 1.评论分析 进入到网易严选官网,搜索“文胸”后,先随便点进一个商品. 在商品页面,打开 Chrome 的控制 ...
- MySQL快速入门及常用命令
数据库 笔记内容 SQL语言共分为四大类:数据查询语言DQL,数据操纵语言DML,数据定义语言DDL,数据控制语言DCL. 1. 数据查询语言DQL 数据查询语言DQL基本结构是由SELECT子句,F ...
- BUUCTF--checkin
文件上传文件上传一般验证方式:1.本地js验证(客户端)2.MIME验证(服务端)3.拓展名验证(服务端)4.脚本内容(文件头)验证(服务端) 通常会用到exif_imagetype()函数,这个函数 ...
- 如何在unbuntu 16.04上离线部署openssh
背景:由于部署环境不能联网,为了方便文件传输,需要用到openssh.故实施步骤是,先在可以联网机器上下载离线包,然后用U盘拷贝到部署环境中. 第一步:下载离线包,下载网址:https://packa ...
- gradle入门
gradle入门 简介: Gradle是一个基于Apache Ant和Apache Maven概念的项目自动化构建开源工具.它使用一种基于Groovy的特定领域语言(DSL)来声明项目设置,抛弃了基于 ...
- 【前端_css】RGB 常用颜色列表
转载博客:RGB 常用颜色列表
- flask flask_session,WTForms
一.Flask_session 本质上,就是是cookie 下的session存储在redis中,方便快速取得session from flask import Flask,session from ...
- zz开源 MNN:淘宝在移动 AI 上的实践
开源 MNN:淘宝在移动 AI 上的实践 陈以鎏(离青) 阅读数:40612019 年 6 月 28 日 随着深度学习的快速发展和端侧设备算力的不断提升,原本在云端执行的推理预测工作正在部分迁 ...
- Java的十三个设计模式
OOP三大基本特性 封装 封装,也就是把客观事物封装成抽象的类,并且类可以把自己的属性和方法只让可信的类操作,对不可信的进行信息隐藏. 继承 继承是指这样一种能力,它可以使用现有的类的所有功能,并在无 ...