HBase中的SplitRegionPolicy实现原理及其源码解读

栏目: 数据库 · 发布时间: 6年前

内容简介:我的HBase是使用的是CDH5.15中的版本,其版本对应的是HBase的版本为1.2,后续的分析都是基于该版本的源码做的分析。在HBase的1.2版本中,RegionSplitPolicy的实现子类共有6个,如下类图:

我的HBase是使用的是CDH5.15中的版本,其版本对应的是HBase的版本为1.2,后续的分析都是基于该版本的源码做的分析。

在HBase的1.2版本中,RegionSplitPolicy的实现子类共有6个,如下类图:

HBase中的SplitRegionPolicy实现原理及其源码解读

以下针对这几个拆分策略做单独的说明。

一、RegionSplitPolicy

RegionSplitPolicy是一个抽象类,其做为所有Region拆分策略的父类。在0.94版本以前,默认的拆分策略是ConstantSizeRegionSplitPolicy,在0.94版本以后,默认的拆分策略为IncreasingToUpperBoundRegionSplitPolicy,这个在RegionSplitPolicy的类注释也有说明:

HBase中的SplitRegionPolicy实现原理及其源码解读

在RegionSplitPolicy中,需要重点关注一个方法getSplitPoint(),其返回Region分裂点的逻辑,其实现代码如下:

/**
   * @return the key at which the region should be split, or null
   * if it cannot be split. This will only be called if shouldSplit
   * previously returned true.
   */
  protected byte[] getSplitPoint() {
    byte[] explicitSplitPoint = this.region.getExplicitSplitPoint();
    if (explicitSplitPoint != null) {
      return explicitSplitPoint;
    }
    List<Store> stores = region.getStores();

    byte[] splitPointFromLargestStore = null;
    long largestStoreSize = 0;
    for (Store s : stores) {
      byte[] splitPoint = s.getSplitPoint();
      long storeSize = s.getSize();
      if (splitPoint != null && largestStoreSize < storeSize) {
        splitPointFromLargestStore = splitPoint;
        largestStoreSize = storeSize;
      }
    }

    return splitPointFromLargestStore;
  }

其首先是判断该Resion是否有用户显示定义的分裂点,如果有则使用用户定义的分裂点,如果则没有则取当前Region的Store中Size最大的那个定义的分裂点。

用户通过在HBase Shell中创建表,通过SPLITS或者SPLITS_FILE参数指定,如下:

hbase>create 'test1','f1',SPLITS => ['10','20','30']

则其分裂点的分布如下:

Region_Name     Start_Key     End_Key
r1                            10
r2              10            20
r3              20            30
r4              30

生成4个Regions。

如果分裂点比较多,不方便写在命令行,可将其列到一个文件中如splits.txt,每行写一个分裂Key,如将上面的分裂Key写到文件中如下:

此时通过如下命令指定分裂Key:

hbase>create 'test1','f1',SPLITS_FILE=>'splits.txt'

二、IncreasingToUpperBoundRegionSplitPolicy

从上面的类图也可以看出IncreasingToUpperBoundRegionSplitPolicy是ConstantSizeRegionSplitPolicy的子类,其优化了原来ConstantSizeRegionSplitPolicy只是单一按照Region文件大小(通常默认为10G,其配置控制参数为hbase.hregion.max.filesize)的拆分策略,增加了对当前表的分片数做为判断因子。如果表的分片数为0或者大于100,则切分大小还是以设置的单一Region文件大小为标准;如果分片数在1~99之间,则取 min (单一Region文件大小 , Region增加策略的初使化大小(其可由配置控制参数为hbase.increasing.policy.initial.size指定;如果没有配置该参数,由取值MemStore的缓存刷新值大小的两倍,MemStore缓存刷新值默认其值为128M,即此时取值256M)*  当前Table Region数的3次方)的结果做为拆分控制大小。

确定initialSize大小的代码逻辑如下:

Configuration conf = getConf();
    initialSize = conf.getLong("hbase.increasing.policy.initial.size", -1);
    if (initialSize > 0) {
      return;
    }
    HTableDescriptor desc = region.getTableDesc();
    if (desc != null) {
      initialSize = 2 * desc.getMemStoreFlushSize();
    }
    if (initialSize <= 0) {
      initialSize = 2 * conf.getLong(HConstants.HREGION_MEMSTORE_FLUSH_SIZE,
                                     HTableDescriptor.DEFAULT_MEMSTORE_FLUSH_SIZE);
    }

确定其拆分控制大小的实现方法如下:

/**
   * @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);
  }

要想达到每次拆分大小为10G的标准,则需要经过以下4次拆分:

第一次split:1^3 * 256 = 256MB 
第二次split:2^3 * 256 = 2048MB 
第三次split:3^3 * 256 = 6912MB 
第四次split:4^3 * 256 = 16384MB > 10GB,因此取较小的值10GB 
后面每次split的size都是10GB了

三、SteppingSplitPolicy

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

public 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.
   */
  protected long getSizeToCheck(final int tableRegionsCount) {
    return tableRegionsCount == 1  ? this.initialSize : getDesiredMaxFileSize();
  }
}

其对Region拆分文件大小做了优化,如果只有1个Region的情况下,那第1次的拆分就是256M,后续则按配置的拆分文件大小(10G)做为拆分标准。在IncreasingToUpperBoundRegionSplitPolicy策略中,针对大表的拆分表现很不错,但是针对小表会产生过多的Region,SteppingSplitPolicy则将小表的Region控制在一个合理的范围,对大表的拆分也不影响。

四、KeyPrefixRegionSplitPolicy

根据rowKey的前缀对数据进行分组,以便于将这些数据分到相同的Region中,这里是通过指定rowKey的前多少位作为前缀做为拆分控制参数,其参数控制为通过指定Table的描述参数KeyPrefixRegionSplitPolicy.prefix_length(旧版为prefix_split_key_policy.prefix_length)控制拆分前缀的长度,比如rowKey都是16位的,指定前5位是前缀,那么前5位相同的rowKey在进行region split的时候会分到相同的region中。

获取拆分点的实现原码如下:

@Override
  protected byte[] getSplitPoint() {
    byte[] splitPoint = super.getSplitPoint();
    if (prefixLength > 0 && splitPoint != null && splitPoint.length > 0) {
      // group split keys by a prefix
      return Arrays.copyOf(splitPoint,
          Math.min(prefixLength, splitPoint.length));
    } else {
      return splitPoint;
    }
  }

五、DelimitedKeyPrefixRegionSplitPolicy

DelimitedKeyPrefixRegionSplitPolicy和KeyPrefixRegionSplitPolicy要达到的结果类似,都是通过将Rowkey的部分前缀做这拆分串,将其以这些前缀前头的RowKey,写到相同的Region中;DelimitedKeyPrefixRegionSplitPolicy的实现方式和KeyPrefixRegionSplitPolicy通过指定前缀固定长度的实现不同的是,其是根据RowKey中指定分隔字符做为拆分的,显得更加灵活,如RowKey的值为“userid_eventtype_eventid”,且指定了分隔字符串为下划线"_",则DelimitedKeyPrefixRegionSplitPolicy将取RowKey值中从左往右且第一个分隔字符串之前的字符做为拆分串,在该示例中就是“userid”。其实现代码如下:

@Override
  protected byte[] getSplitPoint() {
    byte[] splitPoint = super.getSplitPoint();
    if (splitPoint != null && delimiter != null) {

      //find the first occurrence of delimiter in split point
      int index = com.google.common.primitives.Bytes.indexOf(splitPoint, delimiter);
      if (index < 0) {
        LOG.warn("Delimiter " + Bytes.toString(delimiter) + "  not found for split key "
            + Bytes.toString(splitPoint));
        return splitPoint;
      }

      // group split keys by a prefix
      return Arrays.copyOf(splitPoint, Math.min(index, splitPoint.length));
    } else {
      return splitPoint;
    }
  }

六、DisabledRegionSplitPolicy

DisabledRegionSplitPolicy就是不使用Region拆分策略,将所有的数据都写到同一个Region中,其实现非常简单,代码如下:

public class DisabledRegionSplitPolicy extends RegionSplitPolicy {
  @Override
  protected boolean shouldSplit() {
    return false;
  }
}

HBase在执行Region拆分之前,都会调用该方法执行检查是否可以拆分,如果不可以则不会执行后面的拆分点的检查了。


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Think Python

Think Python

Allen B. Downey / O'Reilly Media / 2012-8-23 / GBP 29.99

Think Python is an introduction to Python programming for students with no programming experience. It starts with the most basic concepts of programming, and is carefully designed to define all terms ......一起来看看 《Think Python》 这本书的介绍吧!

URL 编码/解码
URL 编码/解码

URL 编码/解码

Markdown 在线编辑器
Markdown 在线编辑器

Markdown 在线编辑器

RGB CMYK 转换工具
RGB CMYK 转换工具

RGB CMYK 互转工具