当 Region 中任一 MemStore 的大小达到了刷新的阈值(hbase.hregion.memstore.flush.size ,默认是128M),会触发该 MemStore 的 Flush。
Region 级别限制:
当一个 Region 中所有的 MemStore 大小总和达到了上限(hbase.hregion.memstore.block.multiplier * hbase.hregion.memstore.flush.size,默认是 4 * 128M ),会触发该 Region 里面所有的 MemStore Flush 操作,在 Flush 的同时,会阻塞所有写入该 Store 的写请求,继续对该 Region 发送请求的时候,会抛错 region too busy exception 异常。
Region Server 级别限制:
当 Region Server 中的所有 MemStore 大小总和达到了上限 (hbase.regionserver.global.memstore.upperLimit * hbase_heapsize),会触发该 Region Server 的 MemStore Flush,按照 MemStore 的大小由大到小执行 Flush,直到该 Region Server 里所有的 MemStore 低于阈值(hbase.regionserver.global.memstore.lowerLimit * hbase_heapsize)则停止刷新。
Hlog级别限制:
当一个 Region Server 中 HLog 数据达到阈值(hbase.regionserver.maxlogs)时,系统会选取最早的一个 HLog 对应的一个或者多个 Region 进行 Flush。
当文件小于阈值(hbase.hstore.compaction.min.size 默认 128M),则会立即被添加到 Major Compaction 合并的队列,当 HFile 数量超过阈值(hbase.hstore.compaction.min 默认3),则启动 Minor Compaction。
生产上(建议):
hbase.hregion.majorcompaction = 0 ,关闭 Major Compaction。
hbase.hstore.compactionThreshold = 6 (默认3),修改 发生 Minor Compaction 的文件数量。
3. Region Move
在工作中,某个 Region Server 上面如果有很多大的 Region ,且读写请求比较频繁,其他的 Region Server 比较空闲,导致请求不均匀。这时候可以手动 Move Region 到空闲的 Region Server,使集群资源充分利用。
Move 是 Region 的迁移,是一个轻量级的操作,因为本身 HBase 数据存储在 HDFS 上面,不需要独立管理数据,因此 Region 的迁移,真实不需要迁移数据,只是把这个 Region 中的读写服务都放在另一台 Region Server 上面。
Move a region. Optionally specify target regionserver else we choose one
at random. NOTE: You pass the encoded region name, not the region name so
this command is a little different to the others. The encoded region name
is the hash suffix on region names: e.g. if the region name were
TestTable,0094429456,1289497600452.527db22f95c8a9e0116f0cc13c680396. then
the encoded region name portion is 527db22f95c8a9e0116f0cc13c680396
A server name is its host, port plus startcode. For example:
host187.example.com,60020,1289493121758
Examples:
hbase> move 'ENCODED_REGIONNAME'
hbase> move 'ENCODED_REGIONNAME', 'SERVER_NAME'
4. Region Spilt
4.1 HBase Region Spilt 类型
ConstantSizeRegionSplitPolicy:0.94版本前默认切分策略。这是最容易理解但也最容易产生误解的切分策略,从字面意思来看,当 Region 大小大于某个阈值(hbase.hregion.max.filesize)之后就会触发切分,实际上并不是这样,真正实现中这个阈值是对于某个 Store 来说的,即一个 Region 中最大 Store 的大小大于设置阈值之后才会触发切分。另外一个大家比较关心的问题是这里所说的 Store 大小是压缩后的文件总大小还是未压缩文件总大小,实际实现中 Store 大小为压缩后的文件大小(采用压缩的场景)。ConstantSizeRegionSplitPolicy 相对来来说最容易想到,但是在生产线上这种切分策略却有相当大的弊端:切分策略对于大表和小表没有明显的区分。阈值(hbase.hregion.max.filesize)设置较大对大表比较友好,但是小表就有可能不会触发分裂,极端情况下可能就1个,这对业务来说并不是什么好事。如果设置较小则对小表友好,但一个大表就会在整个集群产生大量的region,这对于集群的管理、资源使用、failover来说都不是一件好事。
IncreasingToUpperBoundRegionSplitPolicy: 0.94版本~2.0版本默认切分策略。这种切分策略微微有些复杂,总体来看和 ConstantSizeRegionSplitPolicy 思路相同,一个 Region 中最大 Store 大小大于设置阈值就会触发切分。但是这个阈值并不像 ConstantSizeRegionSplitPolicy 是一个固定的值,而是会在一定条件下不断调整,调整规则和 Region 所属表在当前 Region Server 上的 Region 个数有关系 :(#region个数)^3 * flush size * 2,当然阈值并不会无限增大,最大值为用户设置的 MaxRegionFileSize。这种切分策略很好的弥补了 ConstantSizeRegionSplitPolicy 的短板,能够自适应大表和小表。而且在大集群条件下对于很多大表来说表现很优秀,但并不完美,这种策略下很多小表会在大集群中产生大量小 Region,分散在整个集群中。而且在发生 Region 迁移时也可能会触发 Region 分裂。
SteppingSplitPolicy: 2.0版本默认切分策略。这种切分策略的切分阈值又发生了变化,相比 IncreasingToUpperBoundRegionSplitPolicy 简单了一些,依然和待分裂 Region 所属表在当前 RegionServer 上的 Region 个数有关系,如果 Region 个数等于1,切分阈值为 flush size * 2,否则为 MaxRegionFileSize。这种切分策略对于大集群中的大表、小表会比 IncreasingToUpperBoundRegionSplitPolicy 更加友好,小表不会再产生大量的小 Region,而是适可而止。
建议使用:ConstantSizeRegionSplitPolicy
4.2 HBase Region Spilt 流程
Region In Transition: RIT
Region Server 更改ZK节点 /hbase/region-in-transition 中该 Region 的状态为 SPLITING。
Master通过watch节点 /hbase/region-in-transition 检测到 Region 状态改变,并修改内存中 Region 的状态,在 Master 页面RIT模块就可以看到 Region 执行 Split 的状态信息。
在父存储目录下新建临时文件夹.split保存 Split 后的daughter region信息。
关闭 Parent Region:Parent Region 关闭数据写入并触发 Flush 操作,将写入 Region 的数据全部持久化到磁盘。此后短时间内客户端落在父 Region 上的请求都会抛出异常 NotServingRegionException。
核心分裂步骤:在.split文件夹下新建两个子文件夹,称之为daughter A、daughter B,并在文件夹中生成 reference 文件,分别指向父 Region 中对应文件。
父 Region 分裂为两个子 Region 后,将daughter A、daughter B拷贝到 HBase 根目录下,形成两个新的 Region。
Parent Region 通知修改 hbase.meta 表后下线,不再提供服务。下线后 Parent Region在 meta 表中的信息并不会马上删除,而是标注 Split 列、offline列为true,并记录两个子 Region。
开启daughter A、daughter B两个子 Region 。通知修改 hbase.meta 表,正式对外提供服务。
5. Region Merge
Merge two regions. Passing 'true' as the optional third parameter will force
a merge ('force' merges regardless else merge will fail unless passed
adjacent regions. 'force' is for expert use only).
You can pass the encoded region name or the full region name. The encoded
region name is the hash suffix on region names: e.g. if the region name were
TestTable,0094429456,1289497600452.527db22f95c8a9e0116f0cc13c680396. then
the encoded region name portion is 527db22f95c8a9e0116f0cc13c680396
Examples:
hbase> merge_region 'FULL_REGIONNAME', 'FULL_REGIONNAME'
hbase> merge_region 'FULL_REGIONNAME', 'FULL_REGIONNAME', true
hbase> merge_region 'ENCODED_REGIONNAME', 'ENCODED_REGIONNAME'
hbase> merge_region 'ENCODED_REGIONNAME', 'ENCODED_REGIONNAME', true
5.1 Region Merge 场景
当某个 Region 长时间没有被写入数据,而且该 Region 里面的数据可能是因为 TTL(数据存活时间) 过期而删除,在这种情况下,该 Region 存在没什么意义。一旦空闲的 Region 很多,会导致集群的运维成本上升,所以将相邻的空闲 Region 进行 Merge,可以有效的减少 Region 的数量。
5.2 Region Merge 流程
客户端发送 Merge 请求到 Master。
Master 将待 Merge 的 Region 放在一个 Region Server 里面。
Master 发送合并请求给 Region Server 。
Region Server 接受到 Merge 请求,启动本地事务执行 Merge 操作。
在 Merge 操作中首先是先把两个带合并的 Region 下线,然后再进行 Merge 操作。
将 meta 表中合并前的两个 Region 信息删除,合并结束后再把新的 Region 的信息写入到 meta 表中。
上线新的 Region。
6. RowKey 设计
6.1 RowKey 设计规范
Region 中 有两个重要的指标 SartKey、EndKey,这两个指的是 Region 中 RowKey 的范围。
[hadoop@bigdata ~]$ cd lib/
[hadoop@bigdata lib]$ ll
总用量 88052
-rw-r--r--. 1 hadoop hadoop 90161640 2月 6 21:16 hbase-basic-1.0-jar-with-dependencies.jar
[hadoop@bigdata lib]$ hadoop fs -mkdir /lib
21/02/06 21:17:42 WARN util.NativeCodeLoader: Unable to load native-hadoop library for your platform... using builtin-java classes where applicable
[hadoop@bigdata lib]$ hadoop fs -put hbase-basic-1.0-jar-with-dependencies.jar /lib
21/02/06 21:18:01 WARN util.NativeCodeLoader: Unable to load native-hadoop library for your platform... using builtin-java classes where applicable
添加协处理器
[hadoop@bigdata lib]$ cd ~
[hadoop@bigdata ~]$ cd app/hbase
[hadoop@bigdata hbase]$ bin/hbase shell
hbase(main):001:0> create 'hbasetoes','info'
0 row(s) in 1.4130 seconds
=> Hbase::Table - hbasetoes
hbase(main):002:0> disable 'hbasetoes'
0 row(s) in 2.3290 seconds
hbase(main):003:0> alter 'hbasetoes', METHOD => 'table_att','coprocessor'=>'hdfs://bigdata:9000/lib/hbase-basic-1.0-jar-with-dependencies.jar|com.xk.bigdata.hbase.observer.HBaseElasticsearchObserver|1001|es_cluster=hbasetoes_es,es_host=bigdata,es_port=9200,es_index=hbasetoes_index'
Updating all regions with the new schema...
1/1 regions updated.
Done.
0 row(s) in 1.9500 seconds
hbase(main):004:0> enable 'hbasetoes'
0 row(s) in 4.2700 seconds
hbase(main):005:0> put 'hbasetoes','key1','info:c1','100'
0 row(s) in 1.1430 seconds
hbase(main):006:0> put 'hbasetoes','key1','info:c1','10'
0 row(s) in 0.0090 seconds
查看 HBase Region Server 日志
2021-02-06 22:58:57,314 ERROR [RS_OPEN_REGION-bigdata:60020-0] observer.HBaseElasticsearchObserver: ------observer init EsClient ------clusterName=hbasetoes_es, nodeHost=bigdata, nodePort=9200, indexName=hbasetoes_index, client=org.elasticsearch.client.RestHighLevelClient@e099b57
2021-02-06 22:58:57,315 INFO [RS_OPEN_REGION-bigdata:60020-0] regionserver.RegionCoprocessorHost: Loaded coprocessor com.xk.bigdata.hbase.observer.HBaseElasticsearchObserver from HTD of hbasetoes successfully.
hbase(main):007:0> disable 'hbasetoes'
0 row(s) in 2.2480 seconds
hbase(main):008:0> alter 'hbasetoes', METHOD => 'table_att_unset', NAME => 'coprocessor$1'
Updating all regions with the new schema...
1/1 regions updated.
Done.
0 row(s) in 1.8960 seconds
hbase(main):009:0> enable 'hbasetoes'
0 row(s) in 1.2380 seconds