region split流程分析
region split流程分析
splitregion的发起主要通过client端调用regionserver.splitRegion或memstore.flsuh时检查并发起。
Client通过rpc调用regionserver的splitRegion方法
client端通过HBaseAdmin.split传入regionname与splitpoint(切分的rowkey,能够不传入),
通过meta得到此region所在的server,发起rpc请求,调用HRegionServer.splitRegion方法
publicSplitRegionResponse
splitRegion(finalRpcController
controller,
finalSplitRegionRequest
request)throwsServiceException {
try{
checkOpen();
requestCount.increment();
从onlineRegions中拿到相应的HRegion
HRegion
region=
getRegion(request.getRegion());
region.startRegionOperation(Operation.SPLIT_REGION);
LOG.info("Splitting"
+region.getRegionNameAsString());
在做split前。先对region进行flush操作。请參见regionflush流程分析
region.flushcache();
假设client端发起split请求时指定有split的rowkey,拿到splitkey的值
byte[]splitPoint=
null;
if(request.hasSplitPoint()){
splitPoint=
request.getSplitPoint().toByteArray();
}
设置region的splitRequest属性为true,表示有splitrequest
假设splitrowkey传入不为空,也就是指定有rowkey,设置region的explicitSplitPoint为指定的rowkey
但此值设置后不会被清空,原因后面分析
region.forceSplit(splitPoint);
发起splitrequest,到split的线程进行处理。
Region.checkSplit流程:
a.检查region是否是meta/namespace的region,假设是返回null
b.假设hbase.master.distributed.log.replay配置为true时。同一时候openRegion后此region还没有被replay
region.isRecovering==true,假设是返回null
c.通过hbase.regionserver.region.split.policy配置的RegionSplitPolicy,
默觉得IncreasingToUpperBoundRegionSplitPolicy,
也能够直接在tablecreate时配置SPLIT_POLICY的值为class
调用splitPolicy.shouldSplit(),此方法做例如以下检查流程,并返回true与false
c.1检查region.splitRequest的值是否为true,假设是。返回true,
c.2得到当前regionserver中此region相应的table的全部region个数。
得到region的存储最大size,
c.2.1取出通过tablecreate时的属性MAX_FILESIZE来设置的region最大存储大小,
假设没有取hbase.hregion.max.filesize配置的的值,默觉得10*
1024 * 1024 * 1024L(10g)
c.2.2取出通过tablecreate时的MEMSTORE_FLUSHSIZE属性来设置的regionmemstore的大小,
假设没有取hbase.hregion.memstore.flush.size配置的值,默觉得1024*1024*128L(128M)
c.2.3通过c.2.2的值*(当前rs中此table的region个数*当前rs中此table的region个数)
c.2.4取出c.2.1中得到的值与c.2.3计算出的值最小的一个,得到region最大可存储的size值
c.3检查region中全部store中是否有reference的storefile,假设有返回false
c.4检查region中全部store中全部的storefile的大小是否超过了c.2中得到的size大小,假设是返回true
d.假设c方法调用返回的结果是false,返回null
f.调用RegionSplitPolicy.getSplitPoint()方法返回进行split的切分rowkey
f.1假设region.explicitSplitPoint的值不为空。返回此值
f.2迭代region中全部的store,调用HStore.getSplitPoint()方法得到此store的splitrowkey
HStore.getSplitPoint()方法流程:
调用this.storeEngine.getStoreFileManager().getSplitPoint();得到一个splitpoint
通过hbase.hstore.engine.class配置storeEngine的实现类,默觉得DefaultStoreEngine
默认的storeFileManager为DefaultStoreFileManager,假设store中没有storefile,返回null
否则得到size最大的storefile,并得到此storefile的中间rowkey,并返回此值
g.检查f中得到的rowkey是否在region中,假设不在返回null,否则返回此rowkey,到此checkSplit流程完毕
requestSplit见以下的compactSplitThread.requestSplit(region,rowkey)流程分析
compactSplitThread.requestSplit(region,region.checkSplit());
returnSplitRegionResponse.newBuilder().build();
}catch(IOException
ie){
thrownewServiceException(ie);
}
}
compactSplitThread.requestSplit(region,rowkey)流程
此方法是全部的split请求的终于运行处理程序
publicsynchronized
voidrequestSplit(finalHRegion
r, byte[]midKey) {
假设进行split操作的传入进行切分region的rowkey为null,不做split操作
if(midKey
==null){
LOG.debug("Region"
+ r.getRegionNameAsString()+
"not splittable because midkey=null");
return;
}
try{
生成一个SplitRequest运行线程,通过splits线程池运行此线程,
this.splits.execute(newSplitRequest(r,midKey,this.server));
if(LOG.isDebugEnabled()){
LOG.debug("Splitrequested
for " + r+
". "+
this);
}
}catch(RejectedExecutionException
ree){
LOG.info("Couldnot
execute split for " + r,ree);
}
}
SplitRequest.run方法处理流程:
publicvoid
run(){
if(this.server.isStopping()||
this.server.isStopped()){
LOG.debug("Skippingsplit
because server is stopping="+
this.server.isStopping()+
" or stopped="+
this.server.isStopped());
return;
}
try{
finallongstartTime
=System.currentTimeMillis();
生成一个split运行程序
SplitTransaction
st=
newSplitTransaction(parent,midKey);
//acquirea shared read lock on the table, so that table schema modifications
//donot happen concurrently
tableLock=server.getTableLockManager().readLock(parent.getTableDesc().getTableName()
,
"SPLIT_REGION:"+
parent.getRegionNameAsString());
try{
tableLock.acquire();
}
catch(IOException
ex){
tableLock=
null;
throwex;
}
//If prepare does not return true, for some reason -- logged inside in
//the prepare call -- we are not ready to split just now. Just return.
依据splitkey把原来的region的startkey到splitkey与currenttime生成一个regioninfo
依据splitkey把原来的region的splitkey到endkey与currenttime生成一个regioninfo
if(!st.prepare())return;
try{
运行split操作,通过hbase.regionserver.fileSplitTimeout配置splitfile的timeout时间。默觉得30000ms
在zk中的region-in-transition路径下生成一个依据此region的子路径的RegionTransition实例。
此实例在zk上存储的值为新生成的两个hregioninfo信息,
并设置zk中此节点的EventType为RS_ZK_REQUEST_REGION_SPLIT
在hdfs中的此region文件夹下生成一个.splits文件夹,
关闭当前的region,并得到当前region中全部的store与store下的storefile列表。
从rs中的onlineRegions列表中移出此region
迭代每个store中的全部storefile,生成一个SplitTransaction.StoreFileSplitter实例
通过HRegionFileSystem.splitStoreFile生成一个以splitrow结束的与一个以splitrow开头的Reference的hfile
并存储在切分后的两个新的region的.splits/cfname/storefilename.oldregionname文件
通过调用HRegion(oldregion).createDaughterRegionFromSplits(newregionInfo)生成两个新的HRegion实例
并把.splits文件夹下的hfile文件move到newregion的文件夹下
更新meta表中的信息
生成SplitTransaction.DaughterOpener线程实例。在当前rs中直接通过openregion打开两个新的hregion实例。
把zk中节点的transition的zkEventType从RS_ZK_REGION_SPLITTING更新到RS_ZK_REGION_SPLIT。
通知master在regionserver中的split完毕。等待master对这个消息进行处理。直到master处理完毕。
st.execute(this.server,this.server);
}
catch(Exception
e){
if(this.server.isStopping()||
this.server.isStopped()){
LOG.info(
"Skiprollback/cleanup of failed split of "
+parent.getRegionNameAsString()+
" because server is"
+(this.server.isStopping()?
" stopping":
" stopped"),e);
return;
}
try{
LOG.info("Runningrollback/cleanup
of failed split of "+
parent.getRegionNameAsString()+
"; "+
e.getMessage(),e);
if(st.rollback(this.server,this.server)){
LOG.info("Successfulrollback
of failed split of " +
parent.getRegionNameAsString());
}
else{
this.server.abort("Abort;we
got an error after point-of-no-return");
}
}
catch(RuntimeException
ee){
String
msg=
"Failed rollback of failed splitof " +
parent.getRegionNameAsString()+
" -- aborting server";
//If failed
rollback,kill this server to avoid having a hole in table.
LOG.info(msg,ee);
this.server.abort(msg);
}
return;
}
LOG.info("Regionsplit,
hbase:meta updated, and report to master. Parent="
+parent.getRegionNameAsString()+
", new regions: "
+st.getFirstDaughter().getRegionNameAsString()+
", "
+st.getSecondDaughter().getRegionNameAsString()+
". Split took "
+StringUtils.formatTimeDiff(System.currentTimeMillis(),startTime));
}catch(IOException
ex){
LOG.error("Splitfailed
" + this,RemoteExceptionHandler.checkIOException(ex));
server.checkFileSystem();
}finally{
if(this.parent.getCoprocessorHost()!=
null){
try{
this.parent.getCoprocessorHost().postCompleteSplit();
}
catch(IOException
io){
LOG.error("Splitfailed
" + this,
RemoteExceptionHandler.checkIOException(io));
}
}
releaseTableLock();
}
}
master中处理regionsplit的监听流程
通过AssignmentManager.nodeDataChange事件监听rs中对splitregion的值改动。
nodeDataChanged-->handleAssignmentEvent-->handleRegion
switch(rt.getEventType()){
caseRS_ZK_REQUEST_REGION_SPLIT:
caseRS_ZK_REGION_SPLITTING:
caseRS_ZK_REGION_SPLIT:
设置两个新的region的状态为online状态。并删除zk上的路径
if(!handleRegionSplitting(
rt,encodedName,prettyPrintedRegionName,sn))
{
deleteSplittingNode(encodedName,sn);
}
break;
运行memstore的flush后的split流程分析
在每次运行完毕memstore时,会进行是否须要split的检查。假设须要进行split,会发起splitrequest操作。
privatebooleanflushRegion(finalHRegion
region,finalbooleanemergencyFlush){
...............................................此处省去一些代码
Region.checkSplit流程:
a.检查region是否是meta/namespace的region,假设是返回null
b.假设hbase.master.distributed.log.replay配置为true时。同一时候openRegion后此region还没有被replay
region.isRecovering==true,假设是返回null
c.通过hbase.regionserver.region.split.policy配置的RegionSplitPolicy,
默觉得IncreasingToUpperBoundRegionSplitPolicy,
也能够直接在tablecreate时配置SPLIT_POLICY的值为class
调用splitPolicy.shouldSplit(),此方法做例如以下检查流程。并返回true与false
c.1检查region.splitRequest的值是否为true,假设是,返回true,
c.2得到当前regionserver中此region相应的table的全部region个数,
得到region的存储最大size,
c.2.1取出通过tablecreate时的属性MAX_FILESIZE来设置的region最大存储大小。
假设没有取hbase.hregion.max.filesize配置的的值,默觉得10*
1024 * 1024 * 1024L(10g)
c.2.2取出通过tablecreate时的MEMSTORE_FLUSHSIZE属性来设置的regionmemstore的大小,
假设没有取hbase.hregion.memstore.flush.size配置的值,默觉得1024*1024*128L(128M)
c.2.3通过c.2.2的值*(当前rs中此table的region个数*当前rs中此table的region个数)
c.2.4取出c.2.1中得到的值与c.2.3计算出的值最小的一个,得到region最大可存储的size值
c.3检查region中全部store中是否有reference的storefile。假设有返回false
c.4检查region中全部store中全部的storefile的大小是否超过了c.2中得到的size大小。假设是返回true
d.假设c方法调用返回的结果是false,返回null
f.调用RegionSplitPolicy.getSplitPoint()方法返回进行split的切分rowkey
f.1假设region.explicitSplitPoint的值不为空。返回此值
f.2迭代region中全部的store,调用HStore.getSplitPoint()方法得到此store的splitrowkey
HStore.getSplitPoint()方法流程:
调用this.storeEngine.getStoreFileManager().getSplitPoint();得到一个splitpoint
通过hbase.hstore.engine.class配置storeEngine的实现类,默觉得DefaultStoreEngine
默认的storeFileManager为DefaultStoreFileManager。假设store中没有storefile,返回null
否则得到size最大的storefile,并得到此storefile的中间rowkey,并返回此值
g.检查f中得到的rowkey是否在region中,假设不在返回null,否则返回此rowkey,到此checkSplit流程完毕
此处主要是检查region中是否有store的大小超过了配置的指定大小,也就是对c的检查
booleanshouldSplit=
region.checkSplit()!=
null;
if(shouldSplit){
假设须要做split操作,发起splitrequest
this.server.compactSplitThread.requestSplit(region);
}
elseif(shouldCompact){
server.compactSplitThread.requestSystemCompaction(
region,Thread.currentThread().getName());
}
...............................................此处省去一些代码
returntrue;
}
publicsynchronized
booleanrequestSplit(finalHRegion
r) {
//don't split regions that are blocking
a.检查hbase.regionserver.regionSplitLimit配置的splitlimit是否大于rs中的onlineRegions的个数
减去store中全部的storefile的个是是否大于或等于Store.PRIORITY_USER(1)
if(shouldSplitRegion()&&
r.getCompactPriority()>=
Store.PRIORITY_USER){
假设须要做split操作,得到split的key,此时默认从最大的storefile的中间key開始split
byte[]midKey
=r.checkSplit();
if(midKey
!=null){
发起splitrequest,见compactSplitThread.requestSplit(region,rowkey)流程
requestSplit(r,midKey);
returntrue;
}
}
returnfalse;
}
region split流程分析的更多相关文章
- [HBase]region split流程
1. 简介 HBase 的最小管理单位为region,region会按照region 分裂策略进行分裂. 基于CDH5.4.2 2. 总览
- Solr4.8.0源码分析(25)之SolrCloud的Split流程
Solr4.8.0源码分析(25)之SolrCloud的Split流程(一) 题记:昨天有位网友问我SolrCloud的split的机制是如何的,这个还真不知道,所以今天抽空去看了Split的原理,大 ...
- UserScan的处理流程分析
UserScan的处理流程分析 前置说明 Userscan是通过client或cp中发起的scanner操作. 在Scan中通过caching属性来返回能够返回多少条数据.每次进行next时. 通过b ...
- compact处理流程分析
compact处理流程分析 compact的处理与split同样.由client端与flush时检查发起. 针对compact另一个在rs生成时生成的CompactionChecker线程定期去检查是 ...
- hadoop运行流程分析源代码级
前言: 最近一直在分析hadoop的运行流程,我们查阅了大量的资料,虽然从感性上对这个流程有了一个认识但是我总是感觉对mapreduce的运行还是没有一个全面的认识,所以决定从源代码级别对mapred ...
- spark 启动job的流程分析
从WordCount開始分析 编写一个样例程序 编写一个从HDFS中读取并计算wordcount的样例程序: packageorg.apache.spark.examples importorg.ap ...
- HBase Scan流程分析
HBase Scan流程分析 HBase的读流程目前看来比较复杂,主要由于: HBase的表数据分为多个层次,HRegion->HStore->[HFile,HFile,...,MemSt ...
- cinder侧挂载卷流程分析
cinder侧挂载卷流程分析,存储类型以lvm+iscsi的方式为分析基础cinder侧主要调用了三个接口1)reserve_volume: 把volume的状态改为attaching,阻止其它节点执 ...
- Uboot启动流程分析(二)
1.前言 在前面的文章Uboot启动流程分析(一)中,链接如下: https://www.cnblogs.com/Cqlismy/p/12000889.html 已经简单地分析了low_level_i ...
随机推荐
- 【Luogu】P2324骑士精神(IDA*)
题目链接 当guess>limit-deep的时候return就好了. guess是估价函数,值为不在自己地盘上的骑士个数.limit是本次迭代阈值.deep是已经走了多少步. 这个优化是显然的 ...
- Luogu【P2065】贪心的果农(DP)
题目链接 几乎所有DP题目前本蒟蒻都没有思路.当然包括但不限于这道题.每次都是看了题解然后打的(等价于抄题解)很羞耻 这道题经思考发现,越靠前砍的果树长果子的能力一定越弱,如果长果子的能力一样弱就先把 ...
- BZOJ2246 [SDOI2011]迷宫探险 【记忆化搜索dp + 概率】
题目 输入格式 输出格式 仅包含一个数字,表示在执行最优策略时,人物活着走出迷宫的概率.四舍五入保留3位小数. 输入样例 4 3 3 2 .$. A#B A#C @@@ 143 37 335 85 9 ...
- 刷题总结——选课(ssoj树形dp+记忆化搜索+多叉树转二叉树)
题目: 题目描述 学校实行学分制.每门的必修课都有固定的学分,同时还必须获得相应的选修课程学分.学校开设了 N(N<300)门的选修课程,每个学生可选课程的数量 M 是给定的.学生选修了这M门课 ...
- SHOWMODALDIALOG表单提交时禁止打开新窗口
前提条件:showmodaldialog中有表单form.当action="#"的时候,提交表单,不会打开新窗口,但这种#自提有时不能用,#是本页面完整的带参数的url,如果表单中 ...
- C 语言中的 fgets()
转自:http://blog.csdn.net/daiyutage/article/details/8540932 原型: char * fgets(char * s, int n,FILE *st ...
- 洛谷 [P2734] 游戏
博弈论+区间dp 有博弈论吗?大约只有一个博弈论的壳子 设 dp[i][j] 表示区间 i ~ j 先手最多能取多少, 它可以由 i ~ j - 1 与 i + 1 ~ j 来转移, 等于上述两个区间 ...
- POJ3132 Sum of Different Primes
Time Limit: 5000MS Memory Limit: 65536K Total Submissions: 3473 Accepted: 2154 Description A pos ...
- 【BZOJ1305】dance跳舞(最大流,裂点,二分答案)
题意:一次舞会有n个男孩和n个女孩.每首曲子开始时,所有男孩和女孩恰好配成n对跳交谊舞.每个男孩都不会和同一个女孩跳两首(或更多)舞曲. 有一些男孩女孩相互喜欢,而其他相互不喜欢(不会“单向喜欢”). ...
- 动态添加radiogroup
private LinearLayout layout; //布局 , 可以在xml布局中获得 private RadioGroup group ; //点选按钮组 public void onCre ...