• region split流程分析


    region split流程分析

    splitregion的发起主要通过client端调用regionserver.splitRegionmemstore.flsuh时检查并发起。


    Client通过rpc调用regionserversplitRegion方法

    client端通过HBaseAdmin.split传入regionnamesplitpoint(切分的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请求时指定有splitrowkey,拿到splitkey的值

    byte[]splitPoint= null;

    if(request.hasSplitPoint()){

    splitPoint= request.getSplitPoint().toByteArray();

    }

    设置regionsplitRequest属性为true,表示有splitrequest

    假设splitrowkey传入不为空,也就是指定有rowkey,设置regionexplicitSplitPoint为指定的rowkey

    但此值设置后不会被清空,原因后面分析


    region.forceSplit(splitPoint);

    发起splitrequest,split的线程进行处理。

    Region.checkSplit流程:

    a.检查region是否是meta/namespaceregion,假设是返回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(),此方法做例如以下检查流程,并返回truefalse

    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中此tableregion个数*当前rs中此tableregion个数)

    c.2.4取出c.2.1中得到的值与c.2.3计算出的值最小的一个,得到region最大可存储的size

    c.3检查region中全部store中是否有referencestorefile,假设有返回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()方法得到此storesplitrowkey

    HStore.getSplitPoint()方法流程:

    调用this.storeEngine.getStoreFileManager().getSplitPoint();得到一个splitpoint

    通过hbase.hstore.engine.class配置storeEngine的实现类,默觉得DefaultStoreEngine

    默认的storeFileManagerDefaultStoreFileManager,假设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操作的传入进行切分regionrowkeynull,不做split操作

    if(midKey ==null){

    LOG.debug("Region" + r.getRegionNameAsString()+

    "not splittable because midkey=null");

    return;

    }

    try{

    生成一个SplitRequest运行线程,通过splits线程池运行此线程,

    线程池大小通过hbase.regionserver.thread.split配置,默觉得1

    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把原来的regionstartkeysplitkeycurrenttime生成一个regioninfo

    依据splitkey把原来的regionsplitkeyendkeycurrenttime生成一个regioninfo

    if(!st.prepare())return;

    try{

    运行split操作,通过hbase.regionserver.fileSplitTimeout配置splitfiletimeout时间。默觉得30000ms

    zk中的region-in-transition路径下生成一个依据此region的子路径的RegionTransition实例。

    此实例在zk上存储的值为新生成的两个hregioninfo信息,

    并设置zk中此节点的EventTypeRS_ZK_REQUEST_REGION_SPLIT

    hdfs中的此region文件夹下生成一个.splits文件夹,

    关闭当前的region,并得到当前region中全部的storestore下的storefile列表。

    rs中的onlineRegions列表中移出此region

    迭代每个store中的全部storefile,生成一个SplitTransaction.StoreFileSplitter实例

    通过HRegionFileSystem.splitStoreFile生成一个以splitrow结束的与一个以splitrow开头的Referencehfile

    并存储在切分后的两个新的region.splits/cfname/storefilename.oldregionname文件

    通过调用HRegion(oldregion).createDaughterRegionFromSplits(newregionInfo)生成两个新的HRegion实例

    并把.splits文件夹下的hfile文件movenewregion的文件夹下

    更新meta表中的信息

    生成SplitTransaction.DaughterOpener线程实例。在当前rs中直接通过openregion打开两个新的hregion实例。

    zk中节点的transitionzkEventTypeRS_ZK_REGION_SPLITTING更新到RS_ZK_REGION_SPLIT

    通知masterregionserver中的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;


    运行memstoreflush后的split流程分析

    在每次运行完毕memstore时,会进行是否须要split的检查。假设须要进行split,会发起splitrequest操作。

    privatebooleanflushRegion(finalHRegion region,finalbooleanemergencyFlush){


    ...............................................此处省去一些代码


    Region.checkSplit流程:

    a.检查region是否是meta/namespaceregion,假设是返回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(),此方法做例如以下检查流程。并返回truefalse

    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中此tableregion个数*当前rs中此tableregion个数)

    c.2.4取出c.2.1中得到的值与c.2.3计算出的值最小的一个,得到region最大可存储的size

    c.3检查region中全部store中是否有referencestorefile。假设有返回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()方法得到此storesplitrowkey

    HStore.getSplitPoint()方法流程:

    调用this.storeEngine.getStoreFileManager().getSplitPoint();得到一个splitpoint

    通过hbase.hstore.engine.class配置storeEngine的实现类,默觉得DefaultStoreEngine

    默认的storeFileManagerDefaultStoreFileManager。假设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的个数

    假设不想做split操作。能够把此值设置为一个较小的值。比方1

    b.迭代region下的全部store,检查hbase.hstore.blockingStoreFiles配置的store的文件个数,默觉得7

    减去store中全部的storefile的个是是否大于或等于Store.PRIORITY_USER(1)

    if(shouldSplitRegion()&& r.getCompactPriority()>= Store.PRIORITY_USER){

    假设须要做split操作,得到splitkey,此时默认从最大的storefile的中间key開始split

    byte[]midKey =r.checkSplit();

    if(midKey !=null){

    发起splitrequest,compactSplitThread.requestSplit(region,rowkey)流程

    requestSplit(r,midKey);

    returntrue;

    }

    }

    returnfalse;

    }



  • 相关阅读:
    Crazyflie 2.0 System Architecture
    HDU 4856 Tunnels(BFS+状压DP)
    scp报错:Host key verification failed. REMOTE HOST IDENTIFICATION HAS CHANGED!
    HDU 4175 Class Schedule (暴力+一点dp)
    正則表達式
    匿名訪问之(一)web application级别
    Android UI布局之TableLayout
    cocos2d-x-3.3rc2-003 cocos中的引用计数和自己主动释放池
    一步一步跟我学习hadoop(7)----hadoop连接mysql数据库运行数据读写数据库操作
    hdu Swipe Bo(bfs+状态压缩)错了多次的题
  • 原文地址:https://www.cnblogs.com/tlnshuju/p/6941657.html
Copyright © 2020-2023  润新知