HBase Rowkey设计指南、rowkey设计案例剖析、什么是热点问题 15

前言

如何评判一张HBase表设计的好不好,重点看它的rowkey设计的好不好,所以HBase的rowkey设计是非常重要的。但是rowkey到底是什么?特点如下:

  • 类似于mysql、oracle中的主键,用于标示唯一的行;
  • 完全是由用户指定的一串不重复、唯一的字符串;
  • hbase中的数据永远是根据rowkey的字典顺序来排序的

1. HBase中Rowkey的作用

  • 读写数据时通过rowkey找到对应的Region;
  • MemStore中的数据按Rowkey字典顺序排序;
  • HFile中的数据按Rowkey字典顺序排序。

2. HBase中Rowkey对查询的影响

如果我们的rowkey设计为uid+phone+name,那么这种设计可以很好的支持以下的场景:

  • uid = 111 AND phone = 123 AND name = iteblog
  • uid = 111 AND phone = 123
  • uid = 111 AND phone = 12?
  • uid = 111

但是难以支持如下场景

  • phone = 123 AND name = iteblog
  • phone = 123
  • name = iteblog

3. HBase中Rowkey对Region划分的影响

hbase表的数据是按照rowkey来分散到不同region,不合理的rowkey设计会导致热点问题

什么是热点问题?
热点问题是大量的client直接访问集群的一个或极少数个节点,而集群中的其他节点却处于相对空闲状态。
HBase Rowkey设计指南、rowkey设计案例剖析、什么是热点问题 15
如上图,Region1上的数据是Region2的5倍,这样会导致Region1的访问频率比较高,进而影响这个Region所在机器的其他region。

4. Rowkey的设计技巧

4.1 避免热点的方法 - Salting(加盐)

这里的加盐不是秘密学中的加盐,而是rowkey的前面增加随机数。具体就是给rowkey分配一个随机前缀,以使得它和之前排序不同。分配的前缀种类数量应该和你想使数据分散到不同的region的数量一致。
如果你有一些热点 rowkey反复出现在其他分部均匀的rowkey中,加盐是很有用的。

假如你有下列rowkey,你表中每一个region对应字母表中每一个字母。以‘a’开头是同一个region,以‘b’开头是同一个region。在表中,所有以‘f’开头的都在同一个region,它们的rowkey像下面这样:

foo0001
foo0002
foo0003
foo0004

现在,鸡舍要将上面的这个region里面的数据,分散到4个region中。可以用4个不同的盐:‘a’,‘b’,‘c’,‘d’,在这个方案下,每一个字母前缀都会在不同的region中。加盐之后,你有了下面的rowkey:

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

加盐这种方法增加了写时的吞吐量,但是读时有了额外代价

4.2 避免热点的方法 - Hashing

Hashing的原理是计算rowkey的hash值,然后取hash的部分字符串和原来的rowkey进行拼接。这里说的hash包含MD5、sha1、sha256或sha512等算法。比如我们有如下的rowkey:

foo0001
foo0002
foo0003
foo0004

我们使用md5计算这些rowkey的hash值,然后取前6位和原来的rowkey拼接得到新的rowkey:

95f18cfoo0001
6ccc20foo0002
b61d00foo0003
1a7475foo0004

4.3 避免热点的方法 - Reversing

reversing的原理是反转一段固定长度或者全部的键。比如我们有一些url,并作为rowkey:

flink.iteblog.com
www.iteblog.com
carbondata.iteblog.com
def.iteblog.com

这些url其实属于同一个域名,但是由于前面不一样,导致数据不在一起存放。我们可以对其进行反转,如下:

moc.golbeti.knilf
moc.golbeti.www
moc.golbeti.atadnobrac
moc.golbeti.fed

经过这个之后,这些url的数据就可以放一起了。

5. rowkey 设计案例剖析

5.1 交易类表Rowkey设计

  • 查询某个卖家某段时间内的交易记录
    sellerId + timestamp + orderId
  • 查询某个买家某段时间内的交易记录
    buyerId + timestamp + orderId
  • 根据订单号查询
    orderNo
  • 如果某个商家卖了很多商品,可以如下设计rowkey实现快速搜索
    salt + sellerId + timestamp 其中,salt是随机数。
    可以支持的场景:
    * 全表 scan
    * 按照 sellerId 查询
    * 按照 sellerId + timestamp查询

5.1 金融风控 rowkey 设计

查询某个用户的用户画像数据

  • prefix + uid
  • prefix + idcard
  • prefix + tele
    其中 prefix = substr(md(uid),0,x),x取5-6。uid、idcard以及tele分别表示用户唯一标识符、身份证、手机号码。

5.2 联网 Rowkey 设计

  • 查询某辆车在某个时间范围的交易记录
    carId + timestamp
  • 某批次的车太多,造成热点
    prefix + carId + timestamp 其中 prefix = substr(md5(uid),0 ,x)

5.3 查询最近的数据

查询用户最新的操作记录或者查询用户某段时间的操作记录,RowKey 设计如下:
uid + Long.Max_Value - timestamp
支持的场景

  • 查询用户最新的操作记录
    Scan [uid] startRow [uid][000000000000] stopRow [uid][Long.Max_Value - timestamp]
  • 查询用户某段时间的操作记录
    Scan [uid] startRow [uid][Long.Max_Value – startTime] stopRow [uid][Long.Max_Value - endTime]