实战开发:hbase行键设计及降低region热点的出现

在聊这个问题前,搞明白为什么要设计rowkey?(精心整理的说辞)

Hbase 分布式 列式 内存数据库。有着很高的读写性能。在olap即时分析越来越发挥重要的作用,HBase查询只能通过其Rowkey来查询(Rowkey用来表示唯一一行记录),Rowkey设计的优劣直接影响读写性能。
先谈几个原则:
1.(为什么)Rowkey的长度原则
Rowkey长度设计原则:Rowkey是一个二进制,Rowkey的长度被很多开发者建议说设计在10~100个字节,也有说不超过64k,建议是越短越好。
原因有两点:
其一是HBase的持久化文件HFile是按照KeyValue存储的,如果Rowkey过长比如500个字节,1000万列数据光Rowkey就要占用 500*1000万=50亿个字节,将近5G数据,这会极大影响HFile的存储效率
其二是MemStore缓存部分数据到内存,如果Rowkey字段过长内存的有效利用率会降低,系统无法缓存更多的数据,这会降低检索效率
需要指出的是不仅Rowkey的长度是越短越好,而且列族名、列名等尽量使用短名字,因为HBase属于列式数据库,这些名字都是会写入 到HBase的持久化文件HFile中去,过长的Rowkey、列族、列名都会导致整体的存储量成倍增加。

2.Rowkey的唯一原则(为什么)
必须在设计上保证其唯一性。由于在HBase中数据存储是Key-Value形式,若HBase中同一表插入相同Rowkey,则原先的数据会被覆盖 掉(如果表的version设置为1的话),所以务必保证Rowkey的唯一性。

3.Rowkey的散列原则 (为什么)
我们设计的Rowkey应均匀的分布在各个HBase节点上。拿常见的时间戳举例,假如Rowkey是按系统时间戳的方式递增,Rowkey的第一 部分如果是时间戳信息的话将造成所有新数据都在一个RegionServer上堆积的热点现象,也就是通常说的Region热点问题,热点发生在 大量的client直接访问集中在个别RegionServer上(访问可能是读,写或者其他操作),导致单个RegionServer机器自身负载过高,引起 性能下降甚至Region不可用,常见的是发生jvm full gc或者显示region too busy异常情况,当然这也会影响同一个RegionServer上的其 他Region。

通常有3种办法来解决这个Region热点问题:
ΩΩ1、Reverse反转
针对固定长度的Rowkey反转后存储,这样可以使Rowkey中经常改变的部分放在最前面,可以有效的随机Rowkey。 反转Rowkey的例子通常以手机举例,可以将手机号反转后的字符串作为Rowkey,这样的就避免了以手机号那样比较固定开头(137x、 15x等)导致热点问题, 这样做的缺点是牺牲了Rowkey的有序性。

ΩΩ2、Salt加盐 Salting是将每一个Rowkey加一个前缀,前缀使用一些随机字符,使得数据分散在多个不同的Region,达到Region负载均衡的目标
比如在一个有4个Region(注:以[ ,a)、[a,b)、[b,c)、[c, )为Region起至)的HBase表中, 加Salt前的Rowkey:abc001、abc002、abc003 我们分别加上a、b、c前缀,加Salt后Rowkey为:a-abc001、b-abc002、c-abc003 可以看到,加盐前的Rowkey默认会在第2个region中,加盐后的Rowkey数据会分布在3个region中,理论上处理后的吞吐量应是之前的 3倍。由于前缀是随机的,读这些数据时需要耗费更多的时间,所以Salt增加了写操作的吞吐量,不过缺点是同时增加了读操作的开销。

ΩΩ3、Hash散列或者Mod
md5,其实就是一种算法。可以将一个字符串,或文件,或压缩包,执行md5后,就可以生成一个固定长度为128bit的串。这个串,基本上是唯一
用Hash散列来替代随机Salt前缀的好处是能让一个给定的行有相同的前缀,这在分散了Region负载的同时,使读操作也能够推断。确定 性Hash(比如md5后取前4位做前缀)能让客户端重建完整的RowKey,可以使用get操作直接get想要的行。
例如将上述的原始Rowkey经过hash处理,此处我们采用md5散列算法取前4位做前缀,结果如下 9bf0-abc001 (abc001在md5后是9bf049097142c168c38a94c626eddf3d,取前4位是9bf0) 7006-abc002 95e6-abc003 若以前4个字符作为不同分区的起止,上面几个Rowkey数据会分布在3个region中。实际应用场景是当数据量越来越大的时候,这种设计 会使得分区之间更加均衡。 如果Rowkey是数字类型的,也可以考虑Mod方法。

实际开发我怎么设计的?
比如设计订单状态表时使用:
Rowkey: reverse(order_id) + (Long.MAX_VALUE – timestamp),这样设计的好处一是通过reverse订单号避免Region热点,
二是可以按时间倒 排显示。
如登录、下单等等统称事件(event))的临时存储(HBase只存 储了最近10分钟的热数据)
设计event事件的Rowkey为**:两位随机数Salt + eventId +Date + kafka的Offset**
设计加盐的目的是为了增加查询的并发性,假如Salt的范围是0~n,那我们在查询的时候,可以将数据分为n个split同时做scan操作。经 过我们的多次测试验证,增加并发度能够将整体的查询速度提升5~20倍以上。随后的eventId和Date是用来做范围Scan使用的。在我们
的查询场景中,大部分都是指定了eventId的,因此我们把eventId放在了第二个位置上,同时呢,eventId的取值有几十个,通过Salt + eventId的方式可以保证不会形成热点。在单机部署版本中,HBase会存储所有的event数据,所以我们把date放在rowkey的第三个位置 上以实现按date做scan,批量Scan性能甚至可以做到毫秒级返回。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值