一、Region 自动拆分
HBase 中,表会被划分为1…n 个 Region,被托管在 RegionServer 中。
Region 二个重要的属性:StartKey 与 EndKey 表示这个 Region 维护的 RowKey 范围,当读/写数据时,如果 RowKey 落在某个 start-end key 范围内,那么就会定位到目标region并且读/写到相关的数据。
默认,HBase 在创建表的时候,会自动为表分配一个 Region,正处于混沌时期,start-end key 无边界,所有 RowKey 都往这个 Region里分配。
当数据越来越多,Region 的 size 越来越大时,达到默认的阈值时(根据不同的拆分策略有不同的阈值),HBase 中该 Region 将会进行 split,会找到一个 MidKey 将 Region 一分为二,成为 2 个 Region。而 MidKey 则为这二个 Region 的临界,左为 N 无下界,右为 M 无上界。< MidKey 被分配到 N 区,> MidKey 则会被分配到 M 区。
随着数据量进一步扩大,分裂的两个 Region 达到临界后将重复前面的过程,分裂出更多的 Region。
二、Region 自动拆分策略
Region 的分割操作是不可见的,Master 不会参与其中。RegionServer 拆分 Region的步骤是:先将该 Region 下线,然后拆分,将其子 Region 加入到 META 元信息中,再将他们加入到原本的 RegionServer 中,最后汇报 Master。
执行 split 的线程是 CompactSplitThread。
在 2.0.5 版本中,HBase 提供了 7 种自动拆分策略:
他们之间的继承关系如下:
有三种配置方法:
-
在 hbase-site.xml 中配置,例如:
<property> <name>hbase.regionserver.region.split.policy</name> <value>org.apache.hadoop.hbase.regionserver.IncreasingToUpperBoundRegionSplitPolicy</value> </property>
-
在 HBase Configuration中配置:
private static Configuration conf = HBaseConfiguration.create(); conf.set("hbase.regionserver.region.split.policy", "org.apache.hadoop.hbase.regionserver.SteppingSplitPolicy");
-
在创建表的时候配置:Region 的拆分策略需要根据表的属性来合理的配置, 所以在建表的时候不建议用前两种方式配置,而是针对不同的表设置不同的策略,每种策略在建表时具体使用在解释每种策略的时候说明。
2.1、ConstantSizeRegionSplitPolicy
0.94.0 之前的默认拆分策略,这种策略非常简单,只要 Region 中的任何一个 StoreFile 的大小达到了hbase.hregion.max.filesize
所定义的大小,就进行拆分。
1)相关参数:
hbase.hregion.max.filesize
- default: 10737418240 (10GB)
- description: 当一个 Region 的任何一个 StoreFile 容量达到这个配置定义的大小后,就会拆分 Region
2)部分源码:
拆分的阈值大小可在创建表的时候设置,如果没有设置,就取hbase.hregion.max.filesize
这个配置定义的值,如果这个配置也没有定义,取默认值 10G。
@Override
protected void configureForRegion(HRegion region) {
super.configureForRegion(region);
Configuration conf = getConf();
TableDescriptor desc = region.getTableDescriptor();
if (desc != null) {
// 如果用户在建表时指定了该表的单个Region的上限, 取用户定义的这个值
this.desiredMaxFileSize = desc.getMaxFileSize();
}
if (this.desiredMaxFileSize <= 0) {
// 如果用户没有定义, 取'hbase.hregion.max.filesize'这个配置定义的值, 如果这个配置没有定义, 取默认值 10G
this.desiredMaxFileSize = conf.getLong(HConstants.HREGION_MAX_FILESIZE,
HConstants.DEFAULT_MAX_FILE_SIZE);
}
...
}
// 判断是否进行拆分
@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
if ((!store.canSplit())) {
return false;
}
// Mark if any store is big enough
if (store.getSize() > desiredMaxFileSize) {
foundABigStore = true;
}
}
return foundABigStore || force;
}
3)拆分效果:
经过这种策略的拆分后,Region 的大小是均匀的,例如一个 10G 的Region,拆分为两个 Region 后,这两个新的 Region 的大小是相差不大的,理想状态是每个都是5G。
**ConstantSizeRegionSplitPolicy **切分策略对于大表和小表没有明显的区分,阈值(hbase.hregion.max.filesize):
- 设置较大对大表比较友好,但是小表就有可能不会触发分裂,极端情况下可能就1个,这对业务来说并不是什么好事;
- 设置较小则对小表友好,但大表就会在整个集群产生大量的 Region,这对于集群的管理、资源使用、failover 来说都不是一件好事。
4)创建表时配置:
Connection conn = ConnectionFactory.createConnection(conf);
// 创建一个数据库管理员
Admin admin = conn.getAdmin();
TableName tn = TableName.valueOf(tableName);
// 新建一个表描述,指定拆分策略和最大 StoreFile Size
TableDescriptorBuilder tableBuilder =
TableDescriptorBuilder.newBuilder(tn)
.setRegionSplitPolicyClassName(ConstantSizeRegionSplitPolicy.class.getName())
.setMaxFileSize(1048576000);
// 在表描述里添加列族
for (String columnFamily : columnFamilys) {
tableBuilder.setColumnFamily(ColumnFamilyDescriptorBuilder.newBuilder(Bytes.toBytes(columnFamily)).build());
}
// 根据配置好的表描述建表
admin.createTable(tableBuilder.build());
2.2、IncreasingToUpperBoundRegionSplitPolicy
该策略继承自 ConstantSizeRegionSplitPolicy,是 0.94.0 到 2.0.0 版本的默认策略,其优化了原来 ConstantSizeRegionSplitPolicy 只是单一按照 Region 文件大小的拆分策略,增加了对当前表的分片数作为判断因子。当Region中某个 Store Size 达到 sizeToCheck 阀值时进行拆分,sizeToCheck 计算如下:
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);
}
如果表的分片数为 0 或者大于 100,则切分大小还是以设置的单一 Region 文件大小为标准。如果分片数在 1~99 之间,则由 min(单一 Region 大小, Region 增加策略的初始化大小 * 当前 Table Region 数的3次方) 决定。
Region 增加策略的初始化大小计算如下:
protected void configureForRegion(HRegion region) {
super.configureForRegion(region);
Configuration conf = getConf();
initialSize = conf.getLong("hbase.increasing.policy.initial.size", -1);
if (initialSize > 0) {
return;
}
TableDescriptor desc = region.getTableDescriptor();
if (desc != null