Hbase——Region拆分与合并

2023-03-24  本文已影响0人  小波同学

一、Hbase的Region介绍

Region类似于数据库的分片和分区的概念,每个Region负责一小部分Rowkey范围的数据的读写和维护,Region包含了对应的起始行到结束行的所有信息。master将对应的region分配给不同的RergionServer,由RegionSever来提供Region的读写服务和相关的管理工作。

这部分主要介绍Region实例以及Region的寻找路径:

1.1 region实例

上图模拟了一个Hbase的表是如何拆分成region,以及分配到不同的RegionServer中去。上面是1个Userinfo表,里面有7条记录,其中rowkey为0001到0002的记录被分配到了Region1上,Rowkey为0003到0004的记录被分配到了Region2上,而rowkey为0005、0006和0007的记录则被分配到了Region3上。region1和region2被master分配给了RegionServer1(RS1),Region3被master配分给了RegionServer2(RS2)

备注:这里只是为了更容易的说明拆分的规则,其实真实的场景并不会几条记录拆分到不通的Region上,而是到一定的数据量才会拆分,具体的在Region的拆分那部分再具体的介绍。

1.2 Region的寻址

既然读写都在RegionServer上发生,每个RegionSever为一定数量的region服务,那么client要对某一行数据做读写的时候如何能知道具体要去访问哪个RegionServer呢?那就是接下来我们要讨论的问题。

1.2.1 老的Region寻址方式

在Hbase 0.96版本以前,Hbase有两个特殊的表,分别是-ROOT-表和.META.表,其中-ROOT-的位置存储在ZooKeeper中,-ROOT-本身存储了 .META. Table的RegionInfo信息,并且-ROOT-不会分裂,只有一个region。而.META.表可以被切分成多个region。读取的流程如下图所示:


从上面的路径我们可以看出,用户需要3次请求才能直到用户Table真正的位置,这在一定程序带来了性能的下降。在0.96之前使用3层设计的主要原因是考虑到元数据可能需要很大。但是真正集群运行,元数据的大小其实很容易计算出来。在BigTable的论文中,每行METADATA数据存储大小为1KB左右,如果按照一个Region为128M的计算,3层设计可以支持的Region个数为234个,采用2层设计可以支持217(131072)。那么2层设计的情况下一个 集群可以存储4P的数据。这仅仅是一个Region只有128M的情况下。如果是10G呢? 因此,通过计算,其实2层设计就可以满足集群的需求。因此在0.96版本以后就去掉了-ROOT-表了。

1.2.2 新的Region寻址方式

如上面的计算,2层结构其实完全能满足业务的需求,因此0.96版本以后将-ROOT-表去掉了。如下图所示:


访问路径变成了3步:

总结去掉-ROOT-的原因有如下2点:

注意

二、Region的拆分

2.1 Hbase Region的自动拆分策略

Hbase Region的拆分策略有比较多,比如除了3种默认过的策略,还有DelimitedKeyPrefixRegionSplitPolicy、KeyPrefixRegionSplitPolicy、DisableSplitPolicy等策略,这里着重介绍3种默认的策略。分别是ConstantSizeRegionSplitPolicy策略、IncreasingToUpperBoundRegionSplitPolicy策略和SteppingSplitPolicy策略。

KeyPrefixRegionSplitPolicy

根据rowKey的前缀对数据进行分组,这里是指定rowKey的前多少位作为前缀,比如rowKey都是16位的,指定前5位是前缀,那么前5位相同的rowKey在进行region split的时候会分 到相同的region中。

DelimitedKeyPrefixRegionSplitPolicy

保证相同前缀的数据在同一个region中,例如rowKey的格式为:userid_eventtype_eventid,指定的delimiter为 _ ,则split的的时候会确保userid相同的数据在同一个 region中。

DisabledRegionSplitPolicy

不启用自动拆分, 需要指定手动拆分

ConstantSizeRegionSplitPolicy

固定大小拆分策略:ConstantSizeRegionSplitPolicy策略是0.94版本之前的默认拆分策略。

这个策略的拆分规则是:当region大小达到hbase.hregion.max.filesize(默认10G)后拆分。 这种拆分策略对于小表不太友好,按照默认的设置,如果1个表的Hfile小于10G就一直不会拆分。注意10G是压缩后的大小,如果使用了压缩的话。

如果1个表一直不拆分,访问量小也不会有问题,但是如果这个表访问量比较大的话,就比较容易出现性能问题。这个时候只能手工进行拆分。还是很不方便。

唯一的参数是(hbase-site.xml):

# hbase.hregion.max.filesize:region最大大小,默认为10GB
<property>
    <name>hbase.hregion.max.filesize</name>
    <value>10 * 1024 * 1024 * 1024</value>
</property>

IncreasingToUpperBoundRegionSplitPolicy

动态限制拆分策略:IncreasingToUpperBoundRegionSplitPolicy策略是Hbase的0.94~2.0版本默认的拆分策略,这个策略相较于ConstantSizeRegionSplitPolicy策略做了一些优化,该策略的算法为:min(r^2*flushSize,maxFileSize ),最大为maxFileSize 。

从这个算是我们可以得出flushsize为128M、maxFileSize为10G的情况下,可以计算出Region的分裂情况如下:

从上面的计算我们可以看到这种策略能够自适应大表和小表,但是这种策略会导致小表产生比较多的小region,对于小表还是不是很完美。

涉及到的相关配置有(hbase-site.xml中配置):

#动态限制拆分策略的初始化大小
hbase.increasing.policy.initial.size

#memstore最大刷写值
hbase.hregion.memstore.flush.size

#固定大小拆分策略定义的最大Region大小
hbase.hregion.max.filesize

SteppingSplitPolicy

SteppingSplitPolicy是在Hbase 2.0版本后的默认策略,,拆分规则为:If region=1 then: flush size * 2 else: MaxRegionFileSize。

If region=1 
    then: flush size * 2 
    else: MaxRegionFileSize。

还是以flush size为128M、maxFileSize为10场景为列,计算出Region的分裂情况如下: 第一次拆分大小为:2*128M=256M 第二次拆分大小为:10G

从上面的计算我们可以看出,这种策略兼顾了ConstantSizeRegionSplitPolicy策略和IncreasingToUpperBoundRegionSplitPolicy策略,对于小表也肯呢个比较好的适配。

2.2 Hbase Region拆分的详细流程

Hbase的详细拆分流程图如下:

从上图我们可以看出Region切分的详细流程如下:

注意:为了减少对业务的影响,Region的拆分并不涉及到数据迁移的操作,而只是创建了对父Region的指向。只有在做大合并的时候,才会将数据进行迁移。

那么通过reference文件如何才能查找到对应的数据呢?如下图所示:


2.2 Hbase Region的手动拆分策略

pre-splitting Region预拆分

就是在建表的时候就定义好拆分点的算法,使用org.apache.hadoop.hbase.util.RegionSplitter类来创建表,并传入拆分点算法,就可以在建表同事定义拆分点算法。比如;

hbase org.apache.hadoop.hbase.util.RegionSplitter table_name HexStringSplit -c 10 -f mycf
HexStringSplit:指定的拆分点算法
-c:要拆分的Region数量
-f:要建立的列族名称
#会建立10个固定的Region,使用如下语句查看创建的Region
scan 'hbase:meta',{STARTROW=>'table_name',LIMIT => 10} 

具体的拆分点算法有:

最后调用Bytes.split方法把其实rowkey到结束rowkey之间的长度n等分,然后取每一段的起始和结束作为拆分点
默认预拆分算法只有这两个,我们也可以通过实现SplitAlgorithm接口实现自己的拆分算法,或者干脆手动定出拆分点。

create 'test_split2','mycf2','mysf2',SPLITS=>['AAA','BBB','CCC']
#将表table_name从1000出拆分为两个Region
split 'table_name,c,1476405886999.96dd83893d683','1000'
#其他调用方式有:
split 'tableName'
split 'namespace:tableName'
split 'regionName'#format:'tableName,startKey,id'
split 'tableName','splitKey'
split 'regionName','splitKey'

总结

三、Region的合并

首先,Region的合并(merge)并不是为了性能考虑而是处于维护的目的被创造出来的。比如删了大量的数据,导致每个Region都变小了,这个时候合并Region就比较合适了。

3.1 通过Merge类冷合并Region

通过org.apache.hadoop.hbase.util.Merge类来实现,不需要进入hbase shell,直接执行:

hbase org.apache.hadoop.hbase.util.Merge table_name \
table_name,a,147608089478.39erijidsfd8s098fen32j3i8d9. \
table_name,b,148893879502.48jfidnxoskd023843257822j3i.

就可以实现两个Region的合并,但是有一个前提,必须保证这两个Region已经下线,保证HMaster和所有的HRegionServer都停掉,否则会报错,但是这样太麻烦了,而且不适合生产使用。

3.2 通过online_merge热合并Region

与冷合并不同的是,online_merge的传参是Region的hash值,而Region的hash值就是Region名称的最后那段在两个.之间的字符串部分,需要进入hbase shell:

> merge_region '39erijidsfd8s098fen32j3i8d9','48jfidnxoskd023843257822j3i'
#通过hbase:meta查看Region合并后的信息

3.3 HFile合并(Compact)

MemStore每次刷写都会生成一个HFile,当HFile变多,回到值读取数据磁头寻址缓慢,因为HFile都分散在不同的位置,为了防止寻址动作过多,适当的减少碎片文件,就需要合并HFile。

合并操作主要是在一个Store里边找到需要合并的HFile,然后把它们合并起来,合并在大体意义上有两大类Minor Compation和Major Compaction:

注意:有资料说只有Major合并才会删数据,其实Major合并删除的是带墓碑标记的,而Minor合并直接就不读取TTL过期文件,所以也相当于删除了。

小合并(MinorCompaction)

由前面的刷盘部分的介绍,我们知道当MemStore达到hbase.hregion.memstore.flush.size大小的时候会将数据刷到磁盘,生产StoreFile,因此势必产生很多的小问题,对于Hbase的读取,如果要扫描大量的小文件,会导致性能很差,因此需要将这些小文件合并成大一点的文件。因此所谓的小合并,就是把多个小的StoreFile组合在一起,形成一个较大的StoreFile,通常是累积到3个Store File后执行。通过参数hbase.hstore,compactionThreadhold配置。小合并的大致步骤为:

这种小合并一般速度很快,对业务的影响也比较小。本质上,小合并就是使用短时间的IO消耗以及带宽消耗换取后续查询的低延迟。

大合并(MajorCompaction)

所谓的大合并,就是将一个Region下的所有StoreFile合并成一个StoreFile文件,在大合并的过程中,之前删除的行和过期的版本都会被删除,拆分的母Region的数据也会迁移到拆分后的子Region上。大合并一般一周做一次,控制参数为hbase.hregion.majorcompaction。大合并的影响一般比较大,尽量避免统一时间多个Region进行合并,因此Hbase通过一些参数来进行控制,用于防止多个Region同时进行大合并。该参数为:hbase.hregion.majorcompaction.jitter 具体算法为:

hbase.hregion.majorcompaction参数的值乘于一个随机分数,这个随机分数不能超过hbase.hregion.majorcompaction.jitter的值。hbase.hregion.majorcompaction.jitter的值默认为0.5。 通过hbase.hregion.majorcompaction参数的值加上或减去hbase.hregion.majorcompaction参数的值乘于一个随机分数的值就确定下一次大合并的时间区间。

用户如果想禁用major compaction,只需要将参数hbase.hregion.majorcompaction设为0。建议禁用。

参考:
https://blog.csdn.net/asd136912/article/details/101168177

https://www.cnblogs.com/sx66/p/13425465.html

https://www.cnblogs.com/baran/p/15629009.html

上一篇下一篇

猜你喜欢

热点阅读