HBase2.x精通:结合源码讲解Region的三种Spilt策略

一、概述

    

   最近在工作中接触到split,于是查看了这块的源代码,先看到了split的策略,今天就说说这个吧; 这里我是基于HDP版本的Hadoop集群,对应的HBase的版本为2.2.1,后续的分析都是基于该版本的源码做的分析, HBase-2.x支持7种Region自动拆分的策略,继承关系如下图所示:

图片


二、针对这几种默认拆分策略做单独的说明。


1.RegionSplitPolicy


    RegionSplitPolicy是一个抽象类,其作为所有Region拆分策略的父类。在0.94版本以前,默认的拆分策略是ConstantSizeRegionSplitPolicy,在0.94版本以后,默认的策略为IncreasingToUpperBoundRegionSplitPolicy ,从2.0.0版本开始,默认的策略为SteppingSplitPolicy。


    在基类RegionSplitPolicy的源码中可以看到各个HBase版本默认的Spilt策略:

/** * A split policy determines when a Region should be split. * * @see SteppingSplitPolicy Default split policy since 2.0.0  * @see IncreasingToUpperBoundRegionSplitPolicy Default split policy since *      0.94.0 * @see ConstantSizeRegionSplitPolicy Default split policy before 0.94.0 */@InterfaceAudience.LimitedPrivate(HBaseInterfaceAudience.CONFIG)public abstract class RegionSplitPolicy extends Configured {  ......................}


2.ConstantSizeRegionSplitPolicy


    0.94版本前默认切分策略。这是最容易理解但也最容易产生误解的切分策略,从字面意思来看,当region大小大于某个阈值(由参数hbase.hregion.max.filesize控制)之后就会触发切分,实际上并不是这样,真正实现中这个阈值是对于某个store来说的,即一个region中最大store的大小大于设置阈值之后才会触发切分。另外一个大家比较关心的问题是这里所说的store大小是压缩后的文件总大小还是未压缩文件总大小,实际实现中store大小为压缩后的文件大小(采用压缩的场景)。

    ConstantSizeRegionSplitPolicy相对来来说最容易想到,但是在生产线上这种切分策略却有相当大的弊 端:切分策略对于大表和小表没有明显的区分。阈值(hbase.hregion.max.filesize)设置较大对大表比较友好,但是小表就有可能不会触发分裂,极端情况下可能就1个,这对业务来说并不是什么好事。如果设置较小则对小表友好,但一个大表就会在整个集群产生大量的region,这对于集群的管理、资源使用、failover来说都不是一件好事。


    源码中有一个方法叫shouldSplit,顾名思义就是判断能不能split,ConstantSizeRegionSplitPolicy类中对应的代码如下:

  @Override  protected boolean shouldSplit() {    boolean force = region.shouldForceSplit();    boolean foundABigStore = false;
   for (HStore store : region.getStores()) {      // If any of the stores are unable to split (eg they contain reference files)      // then don't split      //看看有没有引用文件,如果有则不能split      if ((!store.canSplit())) {        return false;      }      // Mark if any store is big enough      //这里就是判断是否大于一个固定值,这里默认就是10G,大于这个值就进行Split,不过hbase有个参数hbase.hregion.max.filesize.jitter      //设置了一个抖动值,这里我没太理解明白,可暂时不考虑,直接认为达到一个固定值10G,      //进行了region的分裂      if (store.getSize() > desiredMaxFileSize) {        foundABigStore = true;      }    }    return foundABigStore || force;  }


3.IncreasingToUpperBoundRegionSplitPolicy


    0.94版本~2.0版本默认切分策略。这种切分策略微微有些复杂,总体来看和ConstantSizeRegionSplitPolicy思路相同,一个region中最大store大小大于设置阈值就会触发切分。但是这个阈值并不像ConstantSizeRegionSplitPolicy是一个固定的值,而是会在一定条件下不断调整,调整规则和region所属表在当前regionserver上的region个数有关系 :(#regions) * (#regions) * (#regions) * flush size * 2,当然阈值并不会无限增大, 最大值为用户设置的MaxRegionFileSize。

    这种切分策略很好的弥补了ConstantSizeRegionSplitPolicy的短板,能够自适应大表和小表。而且在大集群条件下对于很多大表来说表现很优秀,但并不完美,这种策略下很多小表会在大集群中产生大量小region,分散在整个集群中。而且在发生region迁移时也可能会触发region分裂。

    IncreasingToUpperBoundRegionSplitPolicy类中是否分裂判断函数shouldSplit的代码如下:

@Override  protected boolean shouldSplit() {    boolean force = region.shouldForceSplit();    boolean foundABigStore = false;    // Get count of regions that have the same common table as this.region    int tableRegionsCount = getCountOfCommonTableRegions();    //这里就是是否分裂的关键判断逻辑    long sizeToCheck = getSizeToCheck(tableRegionsCount);
   for (HStore store : region.getStores()) {      // If any of the stores is unable to split (eg they contain reference files)      // then don't split      if (!store.canSplit()) {        return false;      }
     // Mark if any store is big enough      long size = store.getSize();      if (size > sizeToCheck) {        LOG.debug("ShouldSplit because " + store.getColumnFamilyName() +          " size=" + StringUtils.humanSize(size) +          ", sizeToCheck=" + StringUtils.humanSize(sizeToCheck) +          ", regionsWithCommonTable=" + tableRegionsCount);        foundABigStore = true;      }    }
   return foundABigStore || force;  }

    这里关键判断逻辑在函数getSizeToCheck中,代码如下:

  /**   * @return Region max size or {@code count of regions cubed * 2 * flushsize},   * which ever is smaller; guard against there being zero regions on this server.   */  protected long getSizeToCheck(final int tableRegionsCount) {    // safety check for 100 to avoid numerical overflow in extreme cases    return tableRegionsCount == 0 || tableRegionsCount > 100               ? getDesiredMaxFileSize()               : Math.min(getDesiredMaxFileSize(),                          initialSize * tableRegionsCount * tableRegionsCount * tableRegionsCount);  }

    这是一个三目运算,如果这个table中在线的region个数为0或则大于100,则使用getDesiredMaxFileSize()方法得到这个阀值,否则就使用getDesiredMaxFileSize()得到的阀值和initialSize * (tableRegionsCount的三次方)中小的那一个。函数getDesiredMaxFileSize获取的可以理解成参数hbase.hregion.max.filesize配置的大小,默认是10G,为了更方便理解分裂的过程,这里举个例子:

    比如我们hbase.hregion.max.filesize默认配置的是10G,要想达到10G分裂的标准,前期需要经过以下4次拆分过程:

    第一次split:1^3 * 128*2= 256MB

    第二次split:2^3 * 128*2= 2048MB

    第三次split:3^3 * 128*2 = 6912MB

    第四次split:4^3 * 128*2 = 16384MB > 10GB,因此取较小的值10GB

    后面每次split的size都是10GB了。


4.SteppingSplitPolicy


    2.0版本以后默认切分策略。这种切分策略的切分阈值又发生了变化,相比IncreasingToUpperBoundRegionSplitPolicy简单了一些,依然和待分裂region所属表在当前regionserver上的region个 数有关系,如果只有1个Region的情况下,那第1次的拆分就是256M,后续则按配置的拆分文件大小(hbase.hregion.max.filesize默认值是10G)作为Region拆分标准。

    在IncreasingToUpperBoundRegionSplitPolicy策略中,针对大表的拆分表现很不错,但是针对小表会产生过多的Region,SteppingSplitPolicy则将小表的Region控制在一个合理的范围,对大表的拆分也不影响。


    SteppingSplitPolicy是IncreasingToUpperBoundRegionSplitPolicy的子类,其总共源码只有几行,如下:

/** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements.  See the NOTICE file * distributed with this work for additional information * regarding copyright ownership.  The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License.  You may obtain a copy of the License at * *     http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */package org.apache.hadoop.hbase.regionserver;
import org.apache.yetus.audience.InterfaceAudience;
@InterfaceAudience.Privatepublic class SteppingSplitPolicy extends IncreasingToUpperBoundRegionSplitPolicy {  /**   * @return flushSize * 2 if there's exactly one region of the table in question   * found on this regionserver. Otherwise max file size.   * This allows a table to spread quickly across servers, while avoiding creating   * too many regions.   */  @Override  protected long getSizeToCheck(final int tableRegionsCount) {    return tableRegionsCount == 1  ? this.initialSize : getDesiredMaxFileSize();  }
}


三、Split策略设置


    Region拆分策略可以全局统一配置,也可以为单独的表指定拆分策略,下面整理了以下几种常用Split策略设置方法:


1.HBase的全局Spilt策略,可在hbase-site.xml添加如下属性:

<property>  <name>hbase.regionserver.region.split.policy</name>  <value>org.apache.hadoop.hbase.regionserver.IncreasingToUpperBoundRegionSplitPolicy</value></property>

2.使用JavaApi设置某个表的Split策略,代码如下:

HTableDescriptor tableDesc = new HTableDescriptor("table");tableDesc.setValue(HTableDescriptor.SPLIT_POLICY, ConstantSizeRegionSplitPolicy.class.getName());tableDesc.addFamily(new HColumnDescriptor(Bytes.toBytes("info")));admin.createTable(tableDesc);


3.可在hbase shell控制台中设置某个表的Split策略(常用),命令如下:

hbase> create 'table1', {METADATA => {'SPLIT_POLICY' => 'org.apache.hadoop.hbase.regionserver.ConstantSizeRegionSplitPolicy'}},{NAME => 'info'}

4.用户也可在Configuration设置全局的Split策略,代码如下:

private static Configuration conf = HBaseConfiguration.create();conf.set("hbase.regionserver.region.split.policy", "org.apache.hadoop.hbase.regionserver.SteppingSplitPolicy");

5.上面3中hbase shell控制台命令的设置,可以通过使用的HBaseConfiguration或按表进行全局设置,代码如下:

HTableDescriptor hTableDesc= ...;hTableDesc.setValue(HTableDescriptor.SPLIT_POLICY, MyCustomSplitPolicy.class.getName());


    这里只介绍了HBase的三种默认的Split策略,其他的几种策略不太常用,有兴趣的可以去了解一下,这里不再讲解,感谢关注!!!


猜你喜欢

转载自blog.51cto.com/15080019/2653833