HbaseRowkey设计原则
原⽂:
⼀、引⾔
HBase由于其存储和读写的⾼性能,在OLAP即时分析中越来越发挥重要的作⽤,在易观精细化运营产品--易观⽅⾈也有⼴泛的
应⽤。作为Nosql数据库的⼀员,HBase查询只能通过其Rowkey来查询(Rowkey⽤来表⽰唯⼀⼀⾏记录),Rowkey设计的优劣
直接影响读写性能。HBase中的数据是按照Rowkey的ASCII字典顺序进⾏全局排序的,有伙伴可能对ASCII字典序印象不够深刻
,下⾯举例说明:
假如有5个Rowkey:"012", "0", "123", "234", "3",按ASCII字典排序后的结果为:"0", "012", "123", "234", "3"。
Rowkey排序时会先⽐对两个Rowkey的第⼀个字节,如果相同,然后会⽐对第⼆个字节,依次类推... 对⽐到第X个字节时,已经超出
了其中⼀个Rowkey的长度,短的Rowkey排在前⾯。
由于HBase是通过Rowkey查询的,⼀般Rowkey上都会存⼀些⽐较关键的检索信息,我们需要提前想好数据具体需要如何查询
,根据查询⽅式进⾏数据存储格式的设计,要避免做全表扫描,因为效率特别低。
⼆、Rowkey设计原则
Rowkey设计应遵循以下原则:
1.Rowkey的唯⼀原则
必须在设计上保证其唯⼀性。由于在HBase中数据存储是Key-Value形式,若HBase中同⼀表插⼊相同Rowkey,则原先的数据
会被覆盖掉(如果表的version设置为1的话),所以务必保证Rowkey的唯⼀性.
2. Rowkey的排序原则
HBase的Rowkey是按照ASCII有序设计的,我们在设计Rowkey时要充分利⽤这点。⽐如视频⽹站上对影⽚《泰坦尼克号》的
弹幕信息,这个弹幕是按照时间倒排序展⽰视频⾥,这个时候我们设计的Rowkey要和时间顺序相关。可以使
⽤"Long.MAX_VALUE - 弹幕发表时间"的 long 值作为 Rowkey 的前缀。
3. Rowkey的散列原则
我们设计的Rowkey应均匀的分布在各个HBase节点上。拿常见的时间戳举例,假如Rowkey是按系统时间戳的⽅式递增,
Rowkey的第⼀部分如果是时间戳信息的话将造成所有新数据都在⼀个RegionServer上堆积的热点现象,也就是通常说的
Region热点问题,热点发⽣在⼤量的client直接访问集中在个别RegionServer上(访问可能是读,写或者其他操作),导致单
个RegionServer机器⾃⾝负载过⾼,引起性能下降甚⾄Region不可⽤,常见的是发⽣jvm full gc或者显⽰region too busy异常情
况,当然这也会影响同⼀个RegionServer上的其他Region。
Region热点问题
1、Reverse反转
针对固定长度的Rowkey反转后存储,这样可以使Rowkey中经常改变的部分放在最前⾯,可以有效的随机Rowkey。
反转Rowkey的例⼦通常以⼿机举例,可以将⼿机号反转后的字符串作为Rowkey,这样的就避免了以⼿机号那样⽐较固定开头(137x、15x等)导致热点问题,这样做的缺点是牺牲了Rowkey的有序性。
2、Salt加盐
Salt是将每⼀个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
⽤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⽅法。
4. Rowkey的长度原则
Rowkey长度设计原则:Rowkey是⼀个⼆进制,Rowkey的长度被很多开发者建议说设计在10~100个字节,建议是越短越好。
原因有两点:
其⼀是HBase的持久化⽂件HFile是按照KeyValue存储的,如果Rowkey过长⽐如500个字节,1000万列
数据光Rowkey就要占
⽤500*1000万=50亿个字节,将近1G数据,这会极⼤影响HFile的存储效率;
其⼆是MemStore缓存部分数据到内存,如果Rowkey字段过长内存的有效利⽤率会降低,系统⽆法缓存更多的数据,这会降低
检索效率;
hbase属于什么数据库需要指出的是不仅Rowkey的长度是越短越好,⽽且列族名、列名等尽量使⽤短名字,因为HBase属于列式数据库,这些名字
都是会写⼊到HBase的持久化⽂件HFile中去,过长的Rowkey、列族、列名都会导致整体的存储量成倍增加。
三、⽅⾈HBase Rowkey设计实战
在实际的设计中我们可能更多的是结合多种设计⽅法来实现Rowkey的最优化设计,⽐如设计订单状态表时使⽤:
Rowkey: reverse(order_id) + (Long.MAX_VALUE – timestamp),这样设计的好处⼀是通过reverse订单号避免Region热点,
⼆是可以按时间倒排显⽰。
结合易观⽅⾈使⽤HBase作为事件(事件指的的终端在APP中发⽣的⾏为,⽐如登录、下单等等统称事件(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性能甚⾄可以做到毫秒级返回。
这样的rowkey设计能够很好的⽀持如下⼏个查询场景:
1、全表scan
在这种情况下,我们仍然可以将全表数据切分成n份并发查询,从⽽实现查询的实时响应。
2、只按照event_id查询
3、按照event_id和date查询
此外易观⽅⾈也使⽤HBase做⽤户画像的标签存储⽅案,存储每个app的⽤户的⼈⼝学属性和商业属性等标签信息,由于其设计的更为复杂,后续会另起篇幅详细展开。最后我们顺带提下HBase的表设计,HBase表设计通常可以是宽表(wide table)模式,即⼀⾏包括很多列。同样的信息也可
以⽤⾼表(tall table)形式存储,通常⾼表的性能⽐宽表要⾼出 50%以上,所以推荐⼤家使⽤⾼表来完成表设计。表设计时,我们也应该要考虑HBase数据库的⼀些特性:
1、在HBase表中是通过Rowkey的字典序来进⾏数据排序的
2、所有存储在HBase表中的数据都是⼆进制的字节
3、原⼦性只在⾏内保证,HBase不⽀持跨⾏事务
4、列族(Column Family)在表创建之前就要定义好
5. 列族中的列标识(Column Qualifier)可以在表创建完以后动态插⼊数据时添加

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。