HBase 的预分区及 rowkey 设计技巧
一、HBase 的预分区
1. 为何要预分区?
增加数据读写效率
负载均衡,防止数据倾斜
方便集群容灾调度 region
优化 Map 数量
2. 如何预分区?
每一个 region 维护着 startRow 与 endRowKey,如果加入的数据符合某个 region 维护的 rowKey 范围,则该数据交给这个 region 维护。
3. 如何设定预分区?
1) 手动指定预分区
完成后如图:
2) 使用 16 进制算法生成预分区
完成后如图:
3) 分区规则创建于文件中
创建 splits.txt 文件内容如下:
vim splits.txt
然后执行:
完成后如图:
4) 使用 JavaAPI 创建预分区
代码如下:
二、HBase 的 rowKey 设计技巧
HBase 是三维有序存储的,通过 rowkey(行键),column key(column family 和 qualifier)和 TimeStamp(时间戳)这个三个维度可以对 HBase 中的数据进行快速定位。
HBase 中 rowkey 可以唯一标识一行记录,在 HBase 查询的时候,有以下几种方式:
通过 get 方式,指定 rowkey 获取唯一一条记录;
通过 scan 方式,设置 startRow 和 stopRow 参数进行范围匹配;
全表扫描,即直接扫描整张表中所有行记录。
1. rowkey 长度原则
rowkey 是一个二进制码流,可以是任意字符串,最大长度 64kb,实际应用中一般为 10-100bytes,以 byte[]形式保存,一般设计成定长。
建议越短越好,不要超过 16 个字节,原因如下:
数据的持久化文件 HFile 中是按照 KeyValue 存储的,如果 rowkey 过长,比如超过 100 字节,1000w 行数据,光 rowkey 就要占用 100*1000w=10 亿个字节,将近 1G 数据,这样会极大影响 HFile 的存储效率;
MemStore 将缓存部分数据到内存,如果 rowkey 字段过长,内存的有效利用率就会降低,系统不能缓存更多的数据,这样会降低检索效率。
2. rowkey 散列原则
如果 rowkey 按照时间戳的方式递增,不要将时间放在二进制码的前面,建议将 rowkey 的高位作为散列字段,由程序随机生成,低位放时间字段,这样将提高数据均衡分布在每个 RegionServer,以实现负载均衡的几率。
如果没有散列字段,首字段直接是时间信息,所有的数据都会集中在一个 RegionServer 上,这样在数据检索的时候负载会集中在个别的 RegionServer 上,造成热点问题,会降低查询效率。
3. rowkey 唯一原则
必须在设计上保证其唯一性,rowkey 是按照字典顺序排序存储的,因此,设计 rowkey 的时候,要充分利用这个排序的特点,将经常读取的数据存储到一块,将最近可能会被访问的数据放到一块。
4. 什么是热点
HBase 中的行是按照 rowkey 的字典顺序排序的,这种设计优化了 scan 操作,可以将相关的行以及会被一起读取的行存取在临近位置,便于 scan。然而糟糕的 rowkey 设计是热点的源头。
热点发生在大量的 client 直接访问集群的一个或极少数个节点(访问可能是读,写或者其他操作)。大量访问会使热点 region 所在的单个机器超出自身承受能力,引起性能下降甚至 region 不可用,这也会影响同一个 RegionServer 上的其他 region,由于主机无法服务其他 region 的请求。
设计良好的数据访问模式以使集群被充分,均衡的利用。为了避免写热点,设计 rowkey 使得不同行在同一个 region,但是在更多数据情况下,数据应该被写入集群的多个 region,而不是一个。
下面是一些常见的避免热点的方法以及它们的优缺点:
1) 加盐
这里所说的加盐不是密码学中的加盐,而是在 rowkey 的前面增加随机数,具体就是给 rowkey 分配一个随机前缀以使得它和之前的 rowkey 的开头不同。分配的前缀种类数量应该和你想使用数据分散到不同的 region 的数量一致。加盐之后的 rowkey 就会根据随机生成的前缀分散到各个 region 上,以避免热点。
2) 哈希
哈希会使同一行永远用一个前缀加盐。哈希也可以使负载分散到整个集群,但是读却是可以预测的。使用确定的哈希可以让客户端重构完整的 rowkey,可以使用 get 操作准确获取某一个行数据。
3) 反转
第三种防止热点的方法时反转固定长度或者数字格式的 rowkey。这样可以使得 rowkey 中经常改变的部分(最没有意义的部分)放在前面。这样可以有效的随机 rowkey,但是牺牲了 rowkey 的有序性。
反转 rowkey 的例子以手机号为 rowkey,可以将手机号反转后的字符串作为 rowkey,这样的就避免了以手机号那样比较固定开头导致热点问题。
3) 时间戳反转
一个常见的数据处理问题是快速获取数据的最近版本,使用反转的时间戳作为 rowkey 的一部分对这个问题十分有用,可以用 Long.Max_Value - timestamp 追加到 key 的末尾,例如 [key][reverse_timestamp] , [key] 的最新值可以通过 scan [key]获得[key]的第一条记录,因为 HBase 中 rowkey 是有序的,第一条记录是最后录入的数据。
其他一些建议:
尽量减少行键和列族的大小在 HBase 中,value 永远和它的 key 一起传输的。当具体的值在系统间传输时,它的 rowkey,列名,时间戳也会一起传输。如果你的 rowkey 和列名很大,这个时候它们将会占用大量的存储空间。
列族尽可能越短越好,最好是一个字符。
冗长的属性名虽然可读性好,但是更短的属性名存储在 HBase 中会更好。
参考文档:
版权声明: 本文为 InfoQ 作者【五分钟学大数据】的原创文章。
原文链接:【http://xie.infoq.cn/article/c825847f732444a186adab34b】。
本文遵守【CC-BY 4.0】协议,转载请保留原文出处及本版权声明。
评论