Hbase预分区
HBase 的数据物理存储格式为多维稀疏排序 Map, 由 key 及 value 组成:
key 的构成: rowkey+column family+column qualifier+timestamp+type
value 的构成:字节形式存储
在 key 中的 rowkey 可以唯一标识一行记录,因此 HBase 的查询有以下几种实现方式:
通过 get 方式,指定 rowkey 获取唯一一条记录;
通过 scan 方式,设置 STARTROW 和 ENDROW 参数进行范围匹配;
全表扫描,即直接扫描整张表中所有行记录。
HBase 是通过 rowkey 来进行查询的,rowkey 设计的优劣会直接影响读写性能。一般 rowkey 上都会存放一些比较关键的检索信息,我们需要提前规划好数据具体要如何查询,根据查询方式进行数据存储格式的设计,避免做效率特别低的全表扫描。
那如何才能设计出既符合业务使用逻辑,又能满足系统性能需求的 rowkey 呢?
1预分区
在介绍 rowkey 设计之前,先来了解 HBase 的预分区,因为预分区跟 rowkey 设计密不可分。rowkey 设计完成后,需要通过预分区来落地实现。
1.1HBase 的 split 机制
通常 HBase 会自动处理 Region 的拆分操作,当 Region 的大小到达一定阈值后,会把过大的 Region 一分为二,之后在两个 Region 中都能继续增长数据。这就是 HBase 的 split 机制。
HBase 默认的 Region split 策略是,根据以下公式确定 split 的 maxFileSize:
min(r2⋅flushSize,maxFileSize)
其中,r为在线 Region 个数,maxFileSize由参数hbase.hregion.max.filesize指定,默认为 10G.
这里假设 flushSize 为 128M,maxFileSize 为默认的 10G,看看 split 的过程:
第一次拆分大小为:min(1*1*128M, 10G)=128M 第二次拆分大小为:min(3*3*128M, 10G)=1152M 第三次拆分大小为:min(5*5*128M, 10G)=3200M 第四次拆分大小为:min(7*7*128M, 10G)=6272M 第五次拆分大小为:min(9*9*128M, 10G)=10G 第六次拆分大小为:min(11*11*128M, 10G)=10G ...
可以看到,只有在第五次之后的拆分大小才为设定的 10G. 因此hbase.hregion.max.filesize 设置得越大,其 split 的上限大小就会越大。
在 HBase 的这个 split 的过程当中,会出现两个问题:
第一,就是我们所说的热点问题(下面会详细介绍),数据会继续往一个 Region 中写,出现写热点问题;
第二,则是拆分合并风暴,当用户的 Region 大小以恒定的速度增长,Region 的拆分会在同一时间发生,因为同时需要压缩 Region 中的存储文件,这个过程会重写拆分后的 Region,这将会引起磁盘 I/O 上升 。
对于拆分合并风暴,通常需要关闭 HBase 的自动管理拆分,然后手动调用 HBase 的 split 和 major_compact,来分散 I/O 负载。但是其中的 split 操作同样是高 I/O 的操作。
1.2 预分区的意义
为了解决这些问题,预分区就是一种很好的方法,通常预分区可以和 rowkey 的设计结合起来使用。
所谓预分区,就是预先创建 HBase 的表分区。在 HBase 中,每一个 Region 维护着 startRowKey 与 endRowKey,如果加入的数据符合某个 Region 维护的 rowkey 范围,则该数据会交给这个 Region 进行维护。因此可以通过预分区,避免出现 split 过程中的热点问题和拆分合并风暴。
在进行预分区之前,需要明确 rowkey 的取值范围和构成逻辑,将数据要存放的分区大致规划好。分区数量建议为 RegionServer 服务器的倍数,这样能保证每个 RegionServer 上的 Region 数量尽可能一样,均衡分布。
例如,rowkey 为:a-abc001、b-abc002、c-abc003,那么预分区就可以设定为:( ,a)、[a,b)、[b,c)、[c, ).
1.3 预分区的方法
1.3.1. 手动设定预分区
可在 HBase Shell 中使用以下语句,创建表的同时手动设定预分区:
create 'default:table', 'CF', 'partition', SPLITS => []
例如,手动设定 10000 - 50000 的预分区:
create 'default:test1', 'info', 'partition1', SPLITS => ['10000','20000','30000','40000','50000']
在 HBase Web 页面上查看新建表的预分区:
图1-3-1:查看手动设定的预分区
可以看到,新建的表 Employee 被预先分成了 6 个分区,每个 RegionServer 各分配到两个分区。
1.3.2. 使用十六进制序列预分区
可在 HBase Shell 中使用以下语句,创建表的同时使用十六进制序列生成预分区:
create 'default:test1', 'info', 'partition1', SPLITS => ['10000','20000','30000','40000','50000']
在 HBase Web 页面上查看新建表的预分区:
图1-3-2:查看十六进制序列预分区
可以看到,新建的表test1 被预先分成了分区