RegionServer columnFamily设计与rowkey设计

RegionServer columnFamily设计与rowkey设计

1 前言

参考官网地址

本文主要讲解region的大小,内存的大小,HDFS的备份因子对Hbase的集群影响,最终的结果就是你实际需要的内存往往比你想象中的要多。

“Lars Hofhansl说,就我个人而言我只会给我Hbase的每台服务节点配置6T左右的磁盘空间(除非你需要面临大量读操作的工作负载),同时Java Heap堆内存保证在32G,保证20个region,128M的memorystore,其它的资源配置保持默认。”


2 关于column family的数量

Hbase不能很好的处理超过2个或者3个Column family,所以请保持你schema创建的时候预先将Column Family的数量保证在一个较小的范围(<=2或者3比较好)。

目前,flush和compaction是在Region级别的基础上进行的,(一个Region代表多个Column Family的连续数据片段(Store)),所以当一个ColumnFamily的一个region中承载大量需要Flush的数据,即使相邻的Column Family需要flush的数据量特别小的时候,此时不管你是那个family,所有family的region数据都会被flush磁盘文件,此时会频繁的产生大量的文件,当大量的Column Family都进行flush数据到StoreFile的时候,此时flush和compaction的交叉作用会造成大量不必要的的IO开销。可以通过将flush和compaction改成Column Family级别,此时只有当该Column Family中需要flush data的 region数量达到一定数量才会进行统一的flush和compaction,这样就很明显减少了IO的次数

如果可以,可以尝试在同一时间只操作一个columnfamily,只有当需要将数据访问细致到column级别的时候才引入使用第二或者第三列簇。

2.1 table中有多个column family情况下行数的影响

当一个table中有多个column family的时候,请注意每个column family的行数,举个栗子。

Table

ColumnFamilyA - 100万行

ColumnFamilyB - 10亿行

因为B的原因,A的数据也会被划分出许多region,而且分布上不同的regionserver上,这使得原本应该scan很快的A出现scan效率低下的情况。

3 Rowkey的设计

rowkey的设计主要是防止热点访问遵循以下特点:

  • 唯一原则
  • 排序原则
  • 散列原则

See schema.casestudies 查看rowkey设计的案例

3.1 热点数据rowkey

row在Hbase中按照rowkey字典顺序排序,这个设计是对scan的一种优化,这就能保证你将相关的数据统一连续存储在一起,scan读取的时候也是一起读取,当在同一时间用户量少的时候比较方便,然而,对于热点hotspotting访问的设计在Hbase中是少之又少的,当多个用户同时传输热点数据时,这些数据都存在集群中的一个或者少量的节点中,这种访问不管是read还是write或者其它的操作,都会压垮管理那些region的机器,造成性能急剧下降,还可能造成region unavalialble或者繁忙的问题。这还会对该regionserver中的其它region造成不利影响,因为该主机已经不发满足用户的负载。

所以设计数据访问模式很重要,这样才能充分,均匀地利用集群

3.2 防止write的时候造成热点

将那些真正需要放在同一个region中的数据的rowkey进行设计,但是从更大的角度讲,要将所有数据写入集群中的多个regions而不是仅仅一个region中。

下面开始介绍一些通用的rowkey设计以及他们的优缺点。

3.2.1加盐salting

加盐简而言之就是将随机数添加在rowkey的开头,保证随机分布存储真实rowkey,需要的前缀的数量根据你想将数据分成多少个region决定。

  • 下面的例子会解释salting怎样将数据写入并存储在不同的region server中,同时会说明该设计对read的负面影响。

假设你有以下的rowkey列表,并且对表进行了与分区,‘a’开头的是一个region,‘b’开头的是一个区域,在此表中,以‘f’开头的rowkey都分配在同一个region中。

案例1
foo0001 
foo0002 
foo0003 
foo0004

现在需要将它们分布在不同的区域中,你决定使用不同的4种盐a,b,c,d,这种情况下这些字母前缀种的每个字母都将根据不同分区的startkey和endkey分布在不同的region,在加盐salting之后,将改成以下rowkey,由于你现在讲数据写入到四个单独的region,从理论上讲,写入的吞吐量是写入一个分区的4倍.

a-foo0003 
b-foo0001 
c-foo0004 
d-foo0002

然后你添加另一行,该行的rowkey与实际已存在的rowkey相同,则会随机为其分配4个可能的盐值之一,如下

a-foo0003 ##
b-foo0001 
c-foo0003 ##
c-foo0004 
d-foo0002
salting缺点

由于分配是随机的,因此,如果按照字典顺序检索row,则需要做更多的操作,这样Get的时候还是会对相同后缀的rowkeys进行很多其它操作(因为实际上相同的rowkey对应的Cell存了多份,我们需要取最新的一份),所以对read不是特别友好,这个得权衡利弊,看是想写入更快还是读的时候更快。

3.2.2 hash散列

相比随机加盐添加前追,你还有一种方式可以将一个给定的row始终按照相同的前缀保持一个被加盐的状态,这样也可以将数据负载均衡到不同的regionserver上防止热点访问,而且保证可以预测的read,使用确定性hash允许客户端重建完整的rowkey,然后使用Get操作精准获取准确的值.

那么什么是hash?

#假如此时有3行rowkey
abc001
abc002
abc003

我们对每个rowkey进行hash,此处我们利用md5算法三列算法对rowkey进行处理取前4位,作为rowkey的前缀,结果如下:

9bf0-abc001 (abc001在md5后是9bf049097142c168c38a94c626eddf3d,取前4位是9bf0)
7006-abc002
95e6-abc003
#如果Rowkey是数据类型,可以使用Mod方法,自行百度吧弟弟们
3.2.3 rowkey反转

第三种防止热点的方式就是将rowkey中的固定长度的字段进行反转或者将数据字段进行反转举个例子:

001abc
002abc
003abc

#将前三位反转,(通常使用变化最频繁的属性进行反转)这样达到了散列分布的效果,但是牺牲了行正常排序的属性
100abc
200abc
300abc

4 Rowkey设计的注意点

  • 减小row或者cell的size大小
    • 在Hbase中value总伴随着描述,一个cell的value在系统中传输的时候,总会伴随着对应的rowkey和column(family) name、timestamp等,如果一个cell的附属信息的大小比实际value都大,会出很多幺蛾子,自己点进来看 ,比如,会让随机快速访问的优化机制终止,原因是cell对应的调制解压器太大了占用了Hbase指定的磁盘空间。
    • 你要想如果rowkey太长,column qualifier太长,这些B会在Hbase中重复存储10亿行 乘以 百万列这么多次,哥哥我是接受不了的
  • Column Family大小尽可能小,最好是一个字符,如“a”
  • attribute,尽管"myVeryImportantAttribute"非常简单易读,但是官方建议"attr"会更好呢,弟弟们,有关HBase内部存储数据的更多信息,请参见KeyValue,以了解为什么这很重要。
  • rowkey-length要尽可能的短,但是要在方便查询的范围内,尽量不要超过16个字节
  • 能用Byte Pattern的时候尽量选择数字类型代替String类型,因为String占用的空间是数字类型的至少3倍,不信的话执行下面的代码。
// long
//
long l = 1234567890L;
byte[] lb = Bytes.toBytes(l);
System.out.println("long bytes length: " + lb.length);   // returns 8

String s = String.valueOf(l);
byte[] sb = Bytes.toBytes(s);
System.out.println("long as string length: " + sb.length);    // returns 10

// hash
//
MessageDigest md = MessageDigest.getInstance("MD5");
byte[] digest = md.digest(Bytes.toBytes(s));
System.out.println("md5 digest bytes length: " + digest.length);    // returns 16

String sDigest = new String(digest);
byte[] sbDigest = Bytes.toBytes(sDigest);
System.out.println("md5 digest as string length: " + sbDigest.length);    // returns 26

5 Rowkey设计之反向时间戳

在Hbase0.98以及以后的版本JavaAPI支持 Reverse Scan APi,减少扫描的时间成本

#以下是rowkey
a
b
c
d
e
f
#此时我只需要扫描e-f的数据,如果正向扫描,我需要扫描所有,如果反向扫描,我 只需要扫描到fe2行,真实爽歪歪

在数据库中常见的问题就是快速查询出最新版本的数据,反向timestamp作为rowkey的一部分可以很好的解决这个问题,具体可以查看Tom White的<<Hadoop:权威指南>>,需要花钱买,弟弟们。

这个技术的具体实现就是将【Long.MAX_VALUE - timestamp】添加在每个rowkey的最后,例如**{rowdy + 【Long.MAX_VALUE - timestamp】}**,该技术主要是用来代替version的,如果不需要,可以忽略

  • 总结:反向时间戳+反向扫描API扫描提升效率,自己可以用 Hbase clientAPI尝试
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值