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

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

内容简介:我的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拆分之前,都会调用该方法执行检查是否可以拆分,如果不可以则不会执行后面的拆分点的检查了。


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

查看所有标签

猜你喜欢:

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

今日简史

今日简史

[以] 尤瓦尔·赫拉利 / 林俊宏 / 中信出版集团 / 2018-8 / 68

四年前,尤瓦尔•赫拉利的《人类简史》横空出世,颠覆了我们关于人类进化的认知。2016年,他的《未来简史》再度惊艳四座,刷新了我们对未来的想象,掀起了全球关于人工智能讨论的新思潮。现在,“简史三部曲”收官之作《今日简史》推出,将目光聚焦到当下,直面今天关乎我们每个人命运的问题和挑战。 尤瓦尔•赫拉利认为,智人之所以能够崛起成为地球的主宰者,主要原因在于其具备了虚构故事的能力。然而,在当前这样一......一起来看看 《今日简史》 这本书的介绍吧!

图片转BASE64编码
图片转BASE64编码

在线图片转Base64编码工具

HEX HSV 转换工具
HEX HSV 转换工具

HEX HSV 互换工具

HSV CMYK 转换工具
HSV CMYK 转换工具

HSV CMYK互换工具