发表于2019-03-192020-03-03 作者 Ryan
首先,我们来看一张 HBase 的存储结构图(图片来源于网络),如下:
HBase 的数据存储节点叫做 HRegionServer,每个 HRegionServer 管理很多个 HRegion,Region 是HBase你们数据管理的最小单元,不同的 Region 存放在不同的 HRegionServer 机器上。
每个 HRegion 由一个 HLog 和 一个或者多个 Store 组成。每个 Store 由保存一个列簇 (columns family) 。 每个Strore又由一个memStore和 零个至多个StoreFile 组成。memStore 存储在内存中, StoreFile 存储在HDFS上。
说到这里,我们可以很明显的感知到,多个 Region 会造成性能问题,具体是什么性能问题,接着往下看……但我们在想一个问题,Region 少一点绕开性能问题行不行。笔者直接告诉你,也不行!为什么? Region 数目太少就会妨碍可扩展性,导致单个 Region 变得很大降低了并行度。
回到正题,我们在定义 HBase 表的时候,在没有配置“hbase.hregion.max.filesize”的情况下,默认单个 Region 的大小为:10 * 1024 * 1024 * 1024,但写入数据量超过该值,会造成 Region 的 split 动作。这个 RegionSplitPolicy 策略有如下几种方式:
- ConstantSizeRegionSplitPolicy : 这个是版本为 0.94.0 的默认region split 策略。根据公式min(r^2*flushSize,maxFileSize ) 确定split的maxFileSize,其中 r 为在线 Region个数,maxFileSize由
hbase.hregion.max.filesize
指定。 - IncreasingToUpperBoundRegionSplitPolicy : 仅仅当region大小超过常量值(
hbase.hregion.max.filesize
大小)时,才进行拆分。 - DelimitedKeyPrefixRegionSplitPolicy : 保证以分隔符前面的前缀为splitPoint,保证相同RowKey前缀的数据在一个Region中 。
- KeyPrefixRegionSplitPolicy :保证具有相同前缀的 Row 在一个Region中(要求设计中前缀具有同样长度)。指定 Rowkey 前缀位数划分 Region ,通过读取 table 的 prefix_split_key_policy.prefix_length 属性,该属性为数字类型,表示前缀长度,在进行 split 时,按此长度对splitPoint进行截取。此种策略比较适合固定前缀的 Rowkey。当table中没有设置该属性,或其属性不为 Integer 类型时,指定此策略效果等同与使用IncreasingToUpperBoundRegionSplitPolicy。
在建表的时候可以直接手动分配分区方式,以避免热点数据,也可以通过配置分区策略,通过配置文件的方式生成的效果是全局的,这点需要注意!
上面说了 HBase 的分区策略,接下来我们回到正题, 进行 Region Split 的条件为:1. 该 Region 下所有的 StoreFile 中最大的 StoreFile 文件的大小超过阀值即进行 Split 在文件层次上,不同的列族,存储在不同的文件中。但是不同的列族,可能会共享一个 Region。(问题点就在这里了)
举个例子:比如你有 A ,B 两个列蔟,A 列蔟 有 1000w 行数据,B 列蔟有 10w 行数据,也就是说 A 列簇存储的数据量是 B 列簇存储数据量的 100 倍。 A 列簇会发生多次 Split 动作,提升 A 列簇的并发性,而 B 列簇由于跟 A 列簇共同属于一个 Region ,也会被动的 Split 掉了! B 列簇被 Split 后,单 Region 所持有的数据量变少了,这样的话,就导致了扫描 B 列簇的性能下降很多。
其次,存在多个列簇的时候,由于它们是共享的同一个 Region,如果其中的一个 列簇触发了 flush 动作,相应关联的其他列簇也会进行 flush 。导致系统整体产生更多的 IO , 耗费资源 。
我们总结一下:
主要有两点:
1. 多列簇导致持有数据量最少的列簇扫描性能下降,主要是因为数据太分散。
2. 系统产生更多的 IO ,耗费资源;