微信公众号:大数据开发运维架构
关注可了解更多大数据相关的资讯。问题或建议,请公众号留言;
如果您觉得“大数据开发运维架构”对你有帮助,欢迎转发朋友圈
从微信公众号拷贝过来,格式有些错乱,建议直接去公众号阅读
一、概述
最近在工作中接触到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.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添加如下属性:
hbase.regionserver.region.split.policy
org.apache.hadoop.hbase.regionserver.IncreasingToUpperBoundRegionSplitPolicy
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策略,其他的几种策略不太常用,有兴趣的可以去了解一下,这里不再讲解,感谢关注!!!
如果觉得我的文章能帮到您,请关注微信公众号“大数据开发运维架构”,并转发朋友圈,谢谢支持!!!