hbase row key设计原则

概念

hbase是 NoSQL 数据库的一种, 常规操作即为增删改查, RowKey,直译为 行键 , 充当了 主键 的作用, 用于标识 唯一 的一行数据

存储

RowKey可以是任意 字符串 ,在HBase内部,RowKey保存为 字节数组 。存储时,数据按照 RowKey的字典序(byte order)排序 存储。设计RowKey时,要充分利用排序存储这个特性,将经常一起读取的行存储放到一起。

特性

  • 行主键, 可以唯一标识一行数据
  • 数据按照RowKey的字典序(byte order)排序存储,因此HBase中的数据永远都是有序的。
  • 可以由用户自己指定,也可以不指定,依赖于自动生成,只要保证这个字符串不重复就可以了

使用到row key的命令

get

通过指定单个RowKey来获取对应的唯一一条记录

like

通过RowKey的range来进行匹配

scan

通过设置 startRowstopRow 参数来进行范围匹配, 如果不设置, 就是 全表扫描

作用

  • Hbase在读写数据时需要通过RowKey找到对应的Region
  • MemStore和HFile中的数据都是按照 RowKey 的字典序排序

设计原则

长度原则

RowKey本质上是一个二进制码的流,可以是任意字符串,最大长度为 64kb ,实际应用中一般为 10-100byte ,以byte[]数组形式保存,一般 设计成定长 。官方建议 越短越好 ,不要超过 16个字节 ,原因可以概括为如下几点:

  • 影响HFile的存储效率 :HBase里的数据在持久化文件HFile中其实是按照Key-Value对形式存储的。这时候如果RowKey很长,比如达到了200byte,那么仅仅1000w行的记录,只考虑RowKey就需占用近2GB的空间,极大的影响了HFile的存储效率。
  • 降低检索效率 :由于MemStore会缓存部分数据到内存中,如果RowKey比较长,就会导致内存的有效利用率降低,也就不能缓存更多的数据,从而降低检索效率。

16字节是64位操作系统的最佳选择:64位系统,内存8字节对齐,控制在16字节,8字节的整数倍利用了操作系统的最佳特性。

唯一原则

由于RowKey用来唯一标识一行记录,所以必须在设计上保证RowKey的唯一性。需要注意:由于HBase中数据存储的格式是Key-Value对格式,所以如果向HBase中同一张表插入相同RowKey的数据,则原先存在的数据会被新的数据给 覆盖 掉(和HashMap效果相同)。

排序原则

HBase会把RowKey按照ASCII进行自然有序排序,所以反过来我们在设计RowKey的时候可以根据这个特点来设计完美的RowKey,好好的利用这个特性就是排序原则。

散列原则

说人话就是咱们设计出的RowKey需要能够 均匀的分布 到各个RegionServer上。

比如设计RowKey的时候,当 Row key 是按时间戳的方式递增,就不要将时间放在二进制码的前面,可以将 Row key 的高位作为散列字段,由程序循环生成,可以在低位放时间字段,这样就可以提高数据均衡分布在每个Region Server实现负载均衡的几率。

数据热点现象

什么是数据热点现象

HBase中的行是按照row key的字典顺序排序的,这种设计优化了 scan 操作,可以将相关的行以及会被一起读取的行存取在临近位置,便于 scan读取。

然而在咱们实际生产中,当大量请求访问HBase集群的一个或少数几个节点,造成少数RegionServer的读写请求过多,负载过大,而其他RegionServer负载却很小,这样就造成热点现象

如何避免

反转(Reversing)

把固定长度或者数字格式的 row key 进行反转,反转分为一般数据反转和时间戳反转,其中以时间戳反转较常见。适用场景如下:

比如咱们初步设计出的RowKey在数据分布上不均匀,但RowKey尾部的数据却呈现出了良好的随机性(注意:随机性强代表经常改变,没意义,但分布较好),此时,可以考虑将RowKey的信息翻转,或者直接将尾部的bytes提前到RowKey的开头。反转可以有效的使RowKey随机分布,但是反转后有序性肯定就得不到保障了,因此它 牺牲了RowKey的有序性 。 因为无法保证有序性, 利于Get操作 ,但 不利于Scan操作 ,因为数据在原RowKey上的自然顺序已经被打乱。

加盐(Salting)

RowKey的加盐和密码学 不一样 ,它的原理是在原RowKey的前面添加固定长度的随机数,也就是给RowKey分配一个随机前缀使它和之前的RowKey的开头不同。适用场景如下:

比如咱们设计的RowKey是有意义的,但是数据类似,随机性比较低,反转也没法保证随机性,这样就没法根据RowKey分配到不同的Region里,这时候就可以使用加盐的方式了。

需要注意随机数要能保障数据在所有Regions间的负载均衡,也就是说分配的随机前缀的种类数量应该和你想把数据分散到的那些region的数量一致。只有这样,加盐之后的row key才会根据随机生成的前缀分散到各个region中,避免了热点现象。

因为添加的是 随机数 ,添加后如果还基于原RowKey查询,就无法知道随机数是什么,那样在查询的时候就需要去各个可能的Region中查找,同时加盐对于读取是 利空的 。并且加盐这种方式增加了读写时的吞吐量。

哈希(Hashing)

这里的哈希是基于RowKey的完整或部分数据进行Hash,而后将哈希后的值完整替换或部分替换原RowKey的前缀部分。这里说的hash常用的有MD5、sha1、sha256 或 sha512 等算法。适用场景如下:

其实哈希和加盐的适用场景类似,但是由于加盐方法的前缀是随机数,用原row key查询时不方便,因此出现了哈希方法,由于哈希是使用各种常见的算法来计算出的前缀,因此哈希既可以使负载分散到整个集群,又可以轻松读取数据。但是与反转类似,哈希也打乱了RowKey的自然顺序,因此也不利于Scan。


hbase row key设计原则
http://www.zhangdeman.cn/archives/hbaseroowkey.html
作者
白茶清欢
发布于
2020年11月3日
许可协议