Hbase好文__结合源码讲解Region的三种Spilt策略

来源微信公众号:HBase工作笔记

结合源码讲解Region的三种Spilt策略

一、概述

   最近在工作中接触到split,于是查看了这块的源代码,先看到了split的策略,
   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.Private
        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.
           */
          @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策略,其他的几种策略不太常用,有兴趣的可以去了解一下,这里不再讲解
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值