1. 首页
  2. 大数据
  3. HBase教程

【HBase教程】(十三)HBase rowkey的设计和预分区

热点问题

我们知道,检索hbase的记录首先要通过row key来定位数据行。有三种方式:

  • 通过get方式,指定rowkey获取唯一一条记录
  • 通过scan方式,设置起始行和结束行参数进行范围匹配
  • 全表扫描,即直接扫描整张表中所有行记录

当大量的client访问hbase集群的一个或少数几个节点,造成少数region server的读/写请求过多、负载过大,而其他region server负载却很小,就造成了“热点”现象。大量访问会使热点region所在的单个主机负载过大,引起性能下降甚至region不可用。
即热点产生原因:有大量连续编号的row key ==> 大量row key相近的记录集中在个别region ==>
client检索记录时,对个别region访问过多 ==> 此region所在的主机过载 ==> 热点问题

既然是因为rowkey访问过多,负载过重而导致,那么我们的切入点就是:尽量均衡地把每一条记录分散到不同的region里去!

RowKey的设计

HBase出现热点问题的主要原因就是rowkey设计的不合理,举个不合理的设计案例:用时间戳生成rowkey,由于时间戳在一段时间内都是连续的,导致在不同的时间段,访问都集中在不同的RegionServer上,比如我要查询1:00的数据,全部集中在了RegionServer1上,我要查询2:00的数据,又全部集中在了RegionServer2上。从而造成热点问题。

所以我们应该合理制定rowkey规则,使rowkey数据随机均匀分布。从以下几个方面设计RowKey:

rowkey散列原则

散列原则非常重要,目的就是让数据均匀分配到每个RegionServer上。

  • 加盐
    这里所说的加盐不是密码学中的加盐,而是在rowkey的前面增加随机数,具体就是给rowkey分配一个随机前缀以使得它和之前的rowkey的开头不同。给多少个前缀?这个数量应该和我们想要分散数据到不同的region的数量一致(类似hive里面的分桶)。加盐之后的rowkey就会根据随机生成的前缀分散到各个region上,以避免热点。
  • 哈希
    尽量要以hash等开头来组织rowkey。因为哈希可以使负载分散到整个集群。hash就是rowkey前面由一串随机字符串组成,随机字符串生成方式可以由SHA或者MD5方式生成,只要region所管理的start-end keys范围比较随机,那么就可以解决写热点问题。例如:
long currentId = 1L;  
byte [] rowkey = Bytes.add(MD5Hash.getMD5AsHex(Bytes.toBytes(currentId))  
                    .substring(0, 8).getBytes(),Bytes.toBytes(currentId));  
  • 反转
    第三种防止热点的方法是反转固定长度或者数字格式的rowkey。这样可以使得rowkey中经常改变的部分(最没有意义的部分)放在前面。这样可以有效的随机rowkey,但是牺牲了rowkey的有序性。
    反转rowkey的例子:以手机号为rowkey,可以将手机号反转后的字符串作为rowkey,从而避免诸如139、158之类的固定号码开头导致的热点问题。
  • 时间戳反转
    一个常见的数据处理问题是快速获取数据的最近版本,使用反转的时间戳作为rowkey的一部分对这个问题十分有用,可以用Long.Max_Value – timestamp追加到key的末尾,例如[key][reverse_timestamp] ,[key] 的最新值可以通过scan [key]获得[key]的第一条记录,因为HBase中rowkey是有序的,第一条记录是最后录入的数据。

rowkey唯一原则

必须在设计上保证其唯一性,rowkey是按照二进制字节数组排序存储的,因此,设计rowkey的时候,要充分利用这个排序的特点,将经常读取的数据存储到一块,将最近可能会被访问的数据放到一块。所以设计rwo key时尽量把体现业务特征的信息、业务上有唯一性的信息编进row key。

rowkey长度原则

rowkey是一个二进制码流,可以是任意字符串,最大长度 64kb ,实际应用中一般为10-100byte,以byte[] 形式保存,一般设计成定长。建议越短越好,不要超过16个字节,2个原因:

  • 数据的持久化文件HFile中是按照(Key,Value)存储的,如果rowkey过长,例如超过100byte,那么1000万行的记录计算,仅row key就需占用近1Gb空间。这样会极大影响HFile的存储效率!
  • MemStore将缓存部分数据到内存,若 rowkey字段过长,内存的有效利用率就会降低,就不能缓存更多的数据,从而降低检索效率。

预分区

我们知道,HBASE在创建表的时候,会自动为表分配一个Region。
但是实际生产环境中,创建新表后,我们往往是需要往表里导入大量的数据。
这样大量的操作就同时集中在了一个RegionServer上,也有了热点问题。而且我们知道,Region达到一定阈值会进行Split,原始只有一个Region,却导入那么多的数据,这样就会导致大量的Split操作,RegionServer可能会出问题。
如何解决这个问题呢?那就是创建表的时候我们就多创建一些Region。这就是预分区
假设我们初始给它10个Region,那么导入大量数据的时候,就会均衡到10个里面,显然比1个Region要好很多。
可是我们应该创建多少个Region呢?显然没有具体答案,要结合业务,根据表的rowkey进行设计。

创建表时设置预分区

通过SPLITS 或者 SPLITS_FILE 可以在创建表的时候指定分区。

hbase(main):005:0> create 'test','info',SPLITS => ['20180201000','20180401000','20180601000']
=> Hbase::Table - test

//如果分区很多,写在命令行就不合适了,那么就写在文件中。

[root@master ~]# cat /home/split.txt 
20180201000
20180401000
20180601000
hbase(main):007:0> create 'test1','info',SPLITS_FILE => '/home/split.txt' 
=> Hbase::Table - test1

通过webUI查看我们对应的region信息
【HBase教程】(十三)HBase rowkey的设计和预分区
我们发现,有4个region,可我们不是创建时只写了3个?没有问题,就是4个。指定x个,最终会有x+1个,第一个没有startkey,最后一个没有endkey。

如何最好的设置预分区

知道了如何设置预分区之后,我们就要思考如何最好的最合理的设置预分区。我们知道,Region的划分是依赖rowkey的,由[Startkey,Endkey)来确定Region。那么我们肯定要先对rowkey进行最优设计。
设计好了rowkey之后,就要想明白数据的key是如何分布的,然后根据数据量、集群规模等因素,规划一下要分成多少region,每个region的startkey和endkey是多少,然后将规划的key写到一个文件中。

总结:合理的Rowkey设计(随机散列)与预分区二者结合起来,是比较完美的。预分区一开始就预建好了一部分region,这些region都维护着自己的start-end keys,在配合上随机散列,写数据能均衡的命中这些预建的region,就能解决上面的那些缺点,大大提供性能。

BDStar原创文章。发布者:Liuyanling,转载请注明出处:http://bigdata-star.com/archives/1256

发表评论

登录后才能评论

联系我们

562373081

在线咨询:点击这里给我发消息

邮件:562373081@qq.com

工作时间:周一至周五,9:30-18:30,节假日休息

QR code