HBase:Region的拆分

为什么要拆分Region

首先,Region是一段Rowkey数据的集合,当查询一条数据时,会先从元数据中判断该条数据的Rowkye属于哪个Region,然后到指定的Region中查找。当一个Region过大时,在这个Region中查找Rowkey的时间也越长。

举个栗子,假设某个region中有100w条数据,现在要在这个region中查找某条数据,则需要遍历这100w条数据;而如果将这个region拆分成10份,那么可以从元数据中知道这条数据属于哪个region,即只要访问拆分后的那个region,即10w条数据。显而易见,在100w条数据中找一条的效率明显没有在10w条数据中找一条高。这也是为什么HBase的效率比MySQL和SQL Server高的原因:传统数据库只把数据放在一个地方,而HBase是将数据分布在多个Region中。

通常较少的region数量可使群集运行的更加平稳,官方指出每个RegionServer大约100regions以内的时候效果最好。因为每个region的每个列簇都有一个写缓存MemStore(默认2M),当region和列簇的数量过多时,内存占用也会提高。而且HBase底层在读取数据时使用的是mapreduce,当region过多时,也会启动大量的map任务。

Region分为自动拆分和手动拆分两种拆分方式

 

自动拆分

根据region大小触发拆分

早期(0.94版本前)HBase仅根据Region中最大的列簇大小(即一个Store,也即HFile)进行自动拆分,默认拆分阈值为10G,当大小达到10G后,将这个region拆分为两个region。参数为:

hbase.hregion.max.filesize

 

根据文件大小动态触发拆分

0.94版本之后,有了IncreasingToUpperBoundRegionSplitPolicy策略。即动态限制region大小上限策略。这也是默认的触发方式。

当region数量较少时,触发切分的阈值也较低,随着region数量提升,触发拆分的阈值也随之提升,但不会超过一个限定的值。

当region的数量在0~100之间,则根据以下公式计算触发拆分的阈值:

Math.min( region数量 ^ 3 * 初始大小, 默认最大文件大小  )

实现代码如下:

/**

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

  }

初始大小由hbase.increasing.policy.initial.size参数控制,如果没有设置的话,则用memstore的刷写大小的2倍,即hbase.hregion.memstore.flush.size * 2。

确定初始大小的代码逻辑为: 

   Configuration conf = getConf();

    initialSize = conf.getLong("hbase.increasing.policy.initial.size", -1);
    //如果设置了,则直接返回

    if (initialSize > 0) {

      return;

    }

    HTableDescriptor desc = region.getTableDesc();
    //如果没有设置或小于0,则为MemStoreFlushSize的两倍

    if (desc != null) {

      initialSize = 2 * desc.getMemStoreFlushSize();

    }

    if (initialSize <= 0) {

      initialSize = 2 * conf.getLong(HConstants.HREGION_MEMSTORE_FLUSH_SIZE,

                                     HTableDescriptor.DEFAULT_MEMSTORE_FLUSH_SIZE);

    }

默认最大文件大小由hbase.hregion.max.filesize参数控制,即Region最大大小。

假如hbase.hregion.memstore.flush.size定义为128MB,那么文件大小的上限增长将是这样:
(1)刚开始只有一个文件的时候,上限是256MB,因为1^3 * 128*2 = 256MB。
(2)当有2个文件的时候,上限是2GB,因为2^3 * 128*2 = 2048MB。
(3)当有3个文件的时候,上限是6.75GB,因为3^3 * 128 * 2 = 6912MB。
(4)以此类推,直到计算出来的上限达到hbase.hregion.max.filesize region所定义的10GB。

Region大小上限的增加如图:

 

当Region个数达到4个的时候由于计算出来的上限已经达到了16GB大于10GB了,所以后面当Region数量再增加的时候文件大小上限已经不会增加了。在最新的版本里IncreasingToUpperBoundRegionSplitPolicy是默认的配置。

 

以长度自定义拆分前缀

KeyPrefixRegionSplitPolicy 是IncreasingToUpperBoundRegionSplitPolicy的子类。在以文件大小触发region拆分的基础上,可以在拆分时根据rowkey的前缀进行拆分。

如:某个region内包含两种前缀的rowkey,分别为:cn001~cn999和us001~us999。当触发了对这个region的拆分时,可能前缀为cn的数据中列较多,数据大小更大,按照默认的拆分方式,将这个region按照文件大小平均拆分成两份,则可能有部分前缀为cn的rowkey会和前缀为us的rowkey被放在同一个新的region,如图:

当使用KeyPrefixRegionSplitPolicy的方式拆分,则结果如下:

KeyPrefixRegionSplitPolicy可以保证相同前缀的rowkey不会被拆分到两个不同的Region里面。通过以下参数来指定rowkey前几个字符为前缀:

KeyPrefixRegionSplitPolicy.prefix_length

这个策略适用的场景是:

1. 数据有多种前缀。

2. 查询多是针对前缀,比较少跨越多个前缀来查询数据。

 

以分隔符自定义拆分前缀

DelimitedKeyPrefixRegionSplitPolicy与上面的KeyPrefixRegionSplitPolicy方式相同,都是在以文件大小为触发拆分的基础上根据rowkey前缀进行拆分。区别在于,KeyPrefixRegionSplitPolicy是以字符串长度指定前缀,而DelimitedKeyPrefixRegionSplitPolicy是以分隔符区分前缀,书中举例:如果以服务器的名字来当前缀,有的服务器叫host12有的叫host1,甚至有的服务器较host101。那么以字符串长度就难以区分各服务器名。

但是可以通过如下参数定义前缀分隔符:

DelimitedKeyPrefixRegionSplitPolicy.delimiter

比如定义了前缀分隔符为_,那么host1_001和host12_999的前缀就分别是host1和host12。

 

热点region拆分策略

上述几种都是根据文件大小为触发条件进行region拆分。但是,假如两个大小相同的region,且都远没有达到触发拆分条件的文件大小,而其中一个region由于包含较多的热点rowkey,导致这个region的访问量远大于另一个region,已经不堪重负,但是region大小离触发拆分条件还遥遥无期,拆分这个region已迫在眉睫。

BusyRegionSplitPolicy就是为了解决这种场景而产生的。

BusyRegionSplitPolicy策略通过以下参数判断哪些region为热点region:

hbase.busy.policy.blockedRequests:请求阻塞率,即请求被阻塞的严重程度。取值范围是0.0~1.0,默认是0.2,即20%的请求被阻塞的意思。

hbase.busy.policy.minAge:拆分最小年龄,当Region的年龄比这个小的时候不拆分,这是为了防止在判断是否要拆分的时候出现了短时间的访问频率波峰,结果没必要拆分的Region被拆分了,因为短时间的波峰会很快地降回到正常水平。单位毫秒,默认值是600000,即10分钟。

hbase.busy.policy.aggWindow:计算是否繁忙的时间窗口,单位毫秒,默认值是300000,即5分钟。用以控制计算的频率。

判断Region是否属于热点region的逻辑如下:

如果“当前时间–上次检测时间>=hbase.busy.policy.aggWindow”,则进行如下计算:这段时间被阻塞的请求/这段时间的总请求 = 请求的被阻塞率(aggBlockedRate),如果“aggBlockedRate >hbase.busy.policy.blockedRequests”,则判断该Region为热点region。

 

禁止拆分region

DisabledRegionSplitPolicy策略会禁止Region自动拆分,但是可以通过手动拆分来拆分Region。

 

预拆分

书中还介绍了预拆分,即在建表时就指定region个数以及拆分条件。书中介绍的是通过命令行创建,但是一般在HBase的web界面创建更加方便,所以不做记录。

 

手动拆分

可以在hbase shell中调用split方法手动拆分region。

split方法的调用方式如下:

split 'tableName'
split 'namespace:tableName'
split 'regionName' # format: 'tableName,startKey,id'
split 'tableName', 'splitKey'
split 'regionName', 'splitKey'

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值