0


HBase的RowKey详解、RowKey设计原则和RowKey优化方法

1、

RowKey

的概念

HBase

RowKey

可以唯一标识一行记录,在

HBase

查询的时候有以下几种方式:

  • 通过 get 方式,指定 RowKey获取唯一一条记录;
  • 通过 scan 方式,设置 startRowstopRow 参数进行范围匹配;
  • 全表扫描,即直接扫描整张表中所有行记录。

从字面意思来看,

RowKey

就是行键的意思,在增删改查的过程中充当了主键的作用。它可以是任意字符串,在 HBase 内部

RowKey

保存为字节数组。

HBase 中的数据是按照

RowKey

ASCII

字典顺序进行全局排序的,有伙伴可能对

ASCII

字典序印象不够深刻,下面举例说明:

假如有5个

Rowkey

"012"

"0"

"123"

"234"

"3"

,按

ASCII

字典排序后的结果为:

"0"

"012"

"123"

"234"

"3"

因此我们设计

Rowkey

时,需要充分利用排序存储这个特性,将经常一起读取的行存储放到一起,要避免做全表扫描,因为效率特别低。

2、

RowKey

设计的三大原则

2.1、

RowKey

唯一原则

RowKey

中数据是以 **

key-value

**格式存储的,

RowKey

可以类比为

MySQL

里面的

key

值,因此在

HBase

的一张表里面,

RowKey

不应该重复。而且一个

RowKey

只能对应一条数据,用

RowKey

get

表里面的数据时,返回的应该是唯一一条对应的数据记录,不应该返回多条。

另外,因为 **

RowKey

是按照字典顺序排序存储的,所以可以将经常读取的数据存储到一块,将最近可能会被访问的数据放到一块。不过这样做虽然方便了

scan

**等范围查询数据,也可能会导致热点问题

2.2、

RowKey

长度原则

RowKey

是一个二进制码流,可以是任意字符串,最大长度

64kb

,实际应用中一般为

10-100bytes

,以

byte[]

形式保存,一般设计成定长。

建议越短越好,不要超过 16 个字节,原因如下:在

HBase

的底层存储

HFile

中,

RowKey

Key-Value

结构中的一个域。假设

RowKey

长度 100B,那么 1000 万条数据中,光

RowKey

就占用掉 100*1000w=10 亿个字节 将近 1G 空间,这样会极大影响

HFile

的存储效率。

过长会导致**

RowKey

memStore

**中占据的内存空间过大,而实际数据占据的空间很小,只写了少量数据就因为

RowKey

占据太多空间而

flush

。因此建议越短越好,不要超过100个字节。

在这里插入图片描述

2.3、排序原则

RowKey

是按照字典顺序排序存储的,因此,设计

RowKey

的时候,要充分利用这个排序的特点,将经常读取的数据存储到一块,将最近可能会被访问的数据放到一块。

一个常见的数据处理问题是快速获取数据的最近版本,使用反转的时间戳作为

RowKey

的一部分对这个问题十分有用,可以用

Long.Max_Value-timestamp

追加到

key

的末尾。

例如

[key][reverse_timestamp],[key]

的最新值可以通过

scan [key]

获得

[key]

的第一条记录,因为

HBase

RowKey 

是有序的,第一条记录是最后录入的数据。

2.4、

RowKey

散列原则

散列原则的作用是将数据打散,不要让连续的数据集中在一个

region

里面,降低热点问题出现的可能性。

# 第一部分数据:
1638584124_user_id
1638584135_user_id
1638584146_user_id
1638584157_user_id
1638584168_user_id
1638584179_user_id

# 第二部分数据:
4214858361Iuser_id
5314858361_user_id
6414858361_user_id
7514858361_user_id
8614858361_user_id
9714858361_user_id

以上面的数据为例,图中的数据就是以时间戳的方式来排序,这样排序如果要访问的数据集中在某一个时间段上,这个时间段内连续的

RowKey

在一个

region

里面(因为**

HBase

里面的数据按照字典排序,连续写入的时候就会让数据因为连续的

RowKey

会进入相同的

region

**里面),对这一个

region

进行频繁的访问就造成了热点问题。因此应该按照散列原则,给时间戳

RowKey

的前面加上随机生成的散列字段,这样连续的时间戳数据也会因为随机的散列字段而进入不同的

region

,避免了热点问题。

当然图上并没有这么做,代码的上半部分数据是原始数据,而代码的下半部分选择将时间戳反转,这样每行

RowKey

的前几位也是各不相同的,不会被写入同一个

region

里面,同样避免了热点问题。

3、

HFile

简单结构示意图

HBase

中设计有

MemStore

BlockCache

,分别对应列族/

Store

级别的写入缓存,

RegionServer

级别的读取缓存。如果

RowKey

字段过长,内存的有效利用率就会降低,系统不能缓存更多的数据,这样会降低检索效率。

在这里插入图片描述

另外,我们目前使用的服务器操作系统都是 64 位系统,内存是按照

8B

对齐的,因此设计

RowKey

时一般做成

8B

的整数倍,如

16B

或者

24B

,可以提高寻址效率。

同样,列族、列名的命名在保证可读的情况下也应尽量短。

value

永远和它的

key

一起传输的。当具体的值在系统间传输时,它的

RowKey

,列名,时间戳也会一起传输(因此实际上列族命名几乎都用一个字母,比如

'c'

'f'

)。如果你的

RowKey

和列名和值相比较很大,那么你将会遇到一些有趣的问题。

HFile

中的索引最终占据了

HBase

分配的大量内存。

4、热点问题

我查看了很多博客,很多博客都只说了连续的

RowKey

在同一个

region

里面就会导致热点问题,其实这样是没有说完的。因为如果只是

RowKey

连续,那么

RegionServer

会自动划分过大的

region

,这样每个

region

里面的数据量也是差不多的,不会因为这样就导致热点问题。

导致热点问题的原因是,

Clinet

访问查询数据时,可能会集中访问某一段连续

RowKey

的数据,而因为

HBase

中是按照字典序升序来排列数据的,这样连续将数据写入表中时,连续

RowKey

的数据就会被划分在同一个

region

里面,而针对这一段连续

RowKey

所在的

region

进行频繁大量的访问,导致

region

所在的节点机器承受了超出自身处理极限的访问量,从而导致效率低下甚至故障。而其他节点存储的数据因为没有被访问,所以一个节点拼死的忙,其他节点围观看戏,这就造成了热点问题。

另外还有一种情况,那就是设置的预分区不合理,同样会导致热点问题,预分区不合理同样可能导致设置的其中一个

region

里面的数据被大量访问。

对于热点问题,应该做的是,对

RowKey

进行合理的设计,让一段连续的

RowKey

进入不同的

region

当中,这样就避免了访问集中在同一个

region

上。

4.1、加盐

RowKey

分配一个随机前缀以使得它和之前的

RowKey

的开头不同。分配的前缀种类数量应该和你想使用数据分散到不同的

region

的数量一致。加盐之后的

RowKey

就会根据随机生成的前缀分散到各个

region

上,从而避免热点问题。

4.2、哈希

针对

RowKey

进行

hash

运算,运算得出的结果,再拼接到原先

RowKey

的前面,这样连续

RowKey

计算得到的

hash

值不相同,将

hash

值与

RowKey

拼接后的新

RowKey

较大概率不会连续,这样就会被送入不同的

region

里面

但是上面也说了,这只是较大概率不会连续,但是连续的

RowKey

计算出来的

hash

值的前缀依旧可能相同。比如一段连续的

RowKey

r1

r2

r3

,这三者经过hash运算后的结果为

aaa

aab

aac

,这样虽然

hash

结果不同,但是hash值的前缀相同,按照字典序排序时依旧是连续的数据。

使用

hash

的好处是,同一个

RowKey

hash

值是固定的,因此查询时只要计算一下

hash

值,依旧可以按照

RowKey

查询数据。而加盐就是给每一个

RowKey

随机加上一个前缀,这就导致同一个

RowKey

,多次加盐的结果也是不同的,因此没办法再用

RowKey

get

到某一条数据(当然非要用

RowKey

去查也可以,用子串过滤器,把原来的

RowKey

作为子串去匹配加盐后的

RowKey

)。

使用

hash

的坏处是,虽然可以继续使用

get

查询,但是因为计算到的

hash

值依旧可能连续,导致热点问题没有被解决。而加盐可以保证解决热点问题,即连续

RowKey

的数据一定被划分到不同的

region

里面。

4.3、反转

第三种防止热点的方法时反转固定长度或者数字格式的

RowKey

。这样可以使得

RowKey

中经常改变的部分放在前面。这样可以有效的随机

RowKey

,但是牺牲了

RowKey

的有序性。

就比如时间戳,大量连续的时间戳只有最后两三位会改变,前面几位基本不会改变,此时就可以将最后两三位提到最前面,将重复的时间戳部分放到后面,避免了连续。

4.4、时间戳反转

时间戳反转这里的反转应该打上引号,因为这里不是将123变成321这样的反转,而是用大数减去小数,用差值作为新的

RowKey

1638620506_uid
1638620512_uid
1638620524_uid
1638620536_uid
1638620548_uid

此时需求是,让最新的记录排在最前面,也就是按照时间戳逆序排序,最新也即最大的时间戳排在最上面方法就是设定一个大数,比如设置一个9999999999的时间戳,然后用这个时间戳去减去上面rowkey里面的时间戳,结果为:

1638620506_uid ——>8,361,379,493_uid
1638620512_uid ——>8,361,379,487_uid
1638620524_uid ——>8,361,379,475_uid
1638620536_uid ——>8,361,379,463_uid
1638620548_uid ——>8,361,379,451_uid

这样再排序的时候,按照字典序排列,最后一条

RowKey

8,361,379,451_uid

就会被放在第一位,实现了最新的一条记录放在最前面的需求。


本文转载自: https://blog.csdn.net/xdx_dili/article/details/133357215
版权归原作者 静惘 所有, 如有侵权,请联系我们删除。

“HBase的RowKey详解、RowKey设计原则和RowKey优化方法”的评论:

还没有评论