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流程分析的更多相关文章

  1. [HBase]region split流程

    1. 简介 HBase 的最小管理单位为region,region会按照region 分裂策略进行分裂. 基于CDH5.4.2 2. 总览

  2. Solr4.8.0源码分析(25)之SolrCloud的Split流程

    Solr4.8.0源码分析(25)之SolrCloud的Split流程(一) 题记:昨天有位网友问我SolrCloud的split的机制是如何的,这个还真不知道,所以今天抽空去看了Split的原理,大 ...

  3. UserScan的处理流程分析

    UserScan的处理流程分析 前置说明 Userscan是通过client或cp中发起的scanner操作. 在Scan中通过caching属性来返回能够返回多少条数据.每次进行next时. 通过b ...

  4. compact处理流程分析

    compact处理流程分析 compact的处理与split同样.由client端与flush时检查发起. 针对compact另一个在rs生成时生成的CompactionChecker线程定期去检查是 ...

  5. hadoop运行流程分析源代码级

    前言: 最近一直在分析hadoop的运行流程,我们查阅了大量的资料,虽然从感性上对这个流程有了一个认识但是我总是感觉对mapreduce的运行还是没有一个全面的认识,所以决定从源代码级别对mapred ...

  6. spark 启动job的流程分析

    从WordCount開始分析 编写一个样例程序 编写一个从HDFS中读取并计算wordcount的样例程序: packageorg.apache.spark.examples importorg.ap ...

  7. HBase Scan流程分析

    HBase Scan流程分析 HBase的读流程目前看来比较复杂,主要由于: HBase的表数据分为多个层次,HRegion->HStore->[HFile,HFile,...,MemSt ...

  8. cinder侧挂载卷流程分析

    cinder侧挂载卷流程分析,存储类型以lvm+iscsi的方式为分析基础cinder侧主要调用了三个接口1)reserve_volume: 把volume的状态改为attaching,阻止其它节点执 ...

  9. Uboot启动流程分析(二)

    1.前言 在前面的文章Uboot启动流程分析(一)中,链接如下: https://www.cnblogs.com/Cqlismy/p/12000889.html 已经简单地分析了low_level_i ...

随机推荐

  1. 对于一棵二叉树,请设计一个算法,创建含有某一深度上所有结点的链表。 给定二叉树的根结点指针TreeNode* root,以及链表上结点的深度,请返回一个链表ListNode,代表该深度上所有结点的值,请按树上从左往右的顺序链接,保证深度不超过树的高度,树上结点的值为非负整数且不超过100000。

    /* struct TreeNode { int val; struct TreeNode *left; struct TreeNode *right; TreeNode(int x) : val(x ...

  2. C# 方法冒号this的用法

    public Class1(string host, int port, string password = null):this() { this.Host=host; this.Port=port ...

  3. BZOJ 3669 [Noi2014]魔法森林 ——SPFA / Link-Cut Tree

    [题目分析] 大意就是有一张图,边权有两个值,ai和bi 找到一条路径,使得路径上的max(ai)+max(bi)最小. 遇到有两个权值或者多个权值的时候,如果他们互相影响,试着用分块搞一搞. 如果互 ...

  4. BZOJ 1015: [JSOI2008]星球大战starwar【并查集】

    题目可以表述成:给定一个无向图G,每次删除它的一个点和与点相关的边集,每次询问该操作后图G的连通度(连通分量的个数).和上一题一样都是考察逆向思维,虽然删除点的做法不会,但是每次加点后询问连通度却是并 ...

  5. Maven常用参数及其说明【转:http://blog.csdn.net/wangjunjun2008/article/details/18982089】

    Maven常用参数及其说明 -h,--help                              Display help information-am,--also-make         ...

  6. 计算系数(codevs 1137)

    题目描述 Description 给定一个多项式(ax + by)^k,请求出多项式展开后x^n y^m项的系数. 输入描述 Input Description 共一行,包含 5 个整数,分别为a,b ...

  7. java.nio.ByteBuffer 以及flip,clear及rewind区别

    Buffer 类 定义了一个可以线性存放primitive type数据的容器接口.Buffer主要包含了与类型(byte, char…)无关的功能. 值得注意的是Buffer及其子类都不是线程安全的 ...

  8. “ORA-01747: user.table.column, table.column 或列说明无效” 的解决方案

    此问题的原因是因为表的列名称使用了Oracle声明的关键字,列名起的不好引起的. 如果列很多,又不好确定是哪个列名使用了关键字,以下建议可供参考: select * from v$reserved_w ...

  9. ASP.NET Core 依赖注入(构造函数注入,属性注入等)

    原文:ASP.NET Core 依赖注入(构造函数注入,属性注入等) 如果你不熟悉ASP.NET Core依赖注入,先阅读文章: 在ASP.NET Core中使用依赖注入   构造函数注入 构造函数注 ...

  10. [Javascript] Wrap fireEvent with fireEventAsync

    The idea is wrap a object with all its function methods and add some additional handling into a new ...