HBase在生产中的RowKey设计

在使用Hbase时,对于表设计时,有两点设计至关重要:列簇的设计和rowkey的设计。

列簇设计:
原则:在合理的范围内能尽量少的减少列簇就减少列簇
最优的设计:将所有相关性很强的key-value都放在同一个列簇下,这样的话就既能做到查询效率最高,也能保证尽可能少的访问不同的磁盘文件(因为一个store保存一个列簇,当有多个列簇时就代表有多个store)。
以用户信息为例,可以将必须的基本信息存放在一个列簇,而一些附加的额外信息可以放在另一个列簇。

RowKey设计:
在HBase中,表会被划分为n个region,被托管在regionserver中,而region有两个重要的属性:StartKey和EndKey,表示这个region维护的rowkey的范围,当我们要读/写数据的时候,如果rowkey落在某一个region的start-end key的范围内,那么就可以快速定为到哪个region并且读/写相关的数据了,
如何快速的定位到我们想要的数据,就在于我们rowkey的设计了!

RowKey的设计三原则:

1、rowkey的长度原则:
Rowkey 是一个二进制码流,Rowkey 的长度被很多开发者建议说设计在 10~100 个字节,不过建议是越短越好,不要超过 16 个字节;
原因如下:

<1>、数据的持久化文件 HFile 中是按照 KeyValue 存储的,如果 Rowkey 过长比如 100 个字节,1000 万列数据光 Rowkey 就要占用 100*1000 万=10 亿个字节,将近 1G 数据,这会极大影响HFile 的存储效率;
<2>、MemStore 将缓存部分数据到内存,如果 Rowkey 字段过长内存的有效利用率会降低,系统将无法缓存更多的数据,这会降低检索效率。因此 Rowkey 的字节长度越短越好;
<3>、目前操作系统是都是 64 位系统,内存 8 字节对齐。控制在 16 个字节,8 字节的整数倍利用操作系统的最佳特性;

2、rowkey的散列原则:
如果 Rowkey 是按时间戳的方式递增,不要将时间放在二进制码的前面,建议将 Rowkey的高位作为散列字段,由程序循环生成,低位放时间字段,这样将提高数据均衡分布在每个Regionserver上 实现负载均衡的几率。如果没有散列字段,首字段直接是时间信息将产生所有新数据都在一个RegionServer 上堆积的热点现象,这样在做数据检索的时候负载将会集中在个别 RegionServer,降低查询效率。

3、rowkey的唯一原则:
必须在设计上保证其唯一性。rowkey 是按照字典顺序排序存储的,因此,设计 rowkey的时候,要充分利用这个排序的特点,将经常读取的数据存储到一块,将最近可能会被访问的数据放到一块。

数据热点:
HBase 中的行是按照 rowkey 的字典顺序排序的,这种设计优化了 scan 操作,可以将相关的行以及会被一起读取的行存取在临近位置,便于 scan。然而糟糕的 rowkey 设计是热点的源头。 热点发生在大量的 client 直接访问集群的一个或极少数个节点(访问可能是读,写或者其他操作)。大量访问会使热点 region 所在的单个机器超出自身承受能力,引起性能下降甚至 region 不可用,这也会影响同一个 RegionServer 上的其他 region,由于主机无法服务其他 region 的请求,设计良好的数据访问模式以使集群被充分,均衡的利用,为了避免写热点,设计rowkey使不同行在同一个region,但是在更多数据情况下,数据应该被写入集群的多个 region,而不是一个。也就是说,数据热点问题提高了读效率,但是降低了写的效率。

防止数据热点的有效措施:
1、加盐:
这里所说的加盐不是密码学中的加盐,而是在 rowkey 的前面增加随机数,具体就是给rowkey 分配一个随机前缀以使得它和之前的 rowkey 的开头不同。分配的前缀种类数量应该和你想使用数据分散到不同的 region 的数量一致。加盐之后的 rowkey 就会根据随机生成的前缀分散到各个 region 上,以避免热点。也就是想分散到50个region上时,就对rowkey的前缀添加1-50的随机数,就可以保证数据是均匀的分散到这50个region上的,但是这样就会造成相邻的数据scan时还会访问其他的region上的数据,降低了读性能,而且对单条数据的get无法进行查询,因为已经不知道rowkey的具体值了,对后期单条记录的查询方面有很大障碍,这种方法仅仅解决了数据存储及写数据性能问题。

2、哈希:
哈希会使同一行永远用一个前缀加盐。哈希也可以使负载分散到整个集群,但是读却是可以预测的。使用确定的哈希可以让客户端重构完整的 rowkey,可以使用 get 操作准确获取某一个行数据,这种方式解决了上述加盐方法的缺陷,但是却牺牲了rowkey带来的排序性能

3、反转:
第三种防止热点的方法是反转固定长度或者数字格式的 rowkey。这样可以使得 rowkey中经常改变的部分(最没有意义的部分)放在前面。这样可以有效的随机 rowkey,但是牺牲了 rowkey 的有序性。反转 rowkey 的例子以手机号为 rowkey,可以将手机号反转后的字符串作为 rowkey,这样的就避免了以手机号那样比较固定开头导致热点问题,而且也可以使用get操作准确的获取某一行数据。

4、时间戳反转:
一个常见的数据处理问题是快速获取数据的最近版本,使用反转的时间戳作为 rowkey的一部分对这个问题十分有用,可以用 Long.Max_Value - timestamp 追加到 key 的末尾,例如 [key][reverse_timestamp] , [key] 的最新值可以通过 scan [key]获得[key]的第一条记录,因为 HBase 中 rowkey 是有序的,第一条记录是最后录入的数据。比如需要保存一个用户的操作记录,按照操作时间倒序排序,在设计 rowkey 的时候,可以这样设计[userId 反转][Long.Max_Value - timestamp],在查询用户的所有操作记录数据的时候,直接指定反转后的userId,startRow是[userId 反 转 ][000000000000],stopRow 是 [userId 反 转][Long.Max_Value - timestamp],如果需要查询某段时间的操作记录,startRow 是[user 反转][Long.Max_Value - 起始时间],stopRow 是[userId 反转][Long.Max_Value - 结束时间]

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值