HBase 是一种分布式、可扩展、海量数据存储的列式存储 NoSQL 数据库,它基于 HDFS,由 ZooKeeper 管理,用于快速访问数据。
HBase 中的每个区域都维护 startrowkey 和 endrowkey,如果添加的数据与某个区域维护的 rowkey 范围匹配,则将数据移交给该区域进行维护。 根据这个原则,我们可以提前大致规划好数据下发的分区,以提高HBase的性能。
HBase 中一条数据的唯一标识符是 rowkey,因此数据存储在哪个分区取决于 rowkey 在哪个预分区,设计 rowkey 的主要目的是将数据均匀分布在所有区域,在一定程度上防止数据倾斜。 接下来,我们来谈谈 Rowkey 的设计原则和常见的设计方案。
1. 设计原则
HBase 中的行按照 rowkey 的字典顺序排序,优化了扫描操作,可以近距离访问相关行和将一起读取的行,方便扫描,但糟糕的 rowkey 设计是数据热点的来源。 当大量客户端直接访问集群中的一个或极少数节点(可能是读、写或其他操作)时,就会出现数据热点。 大量请求会导致热点区域中的单台机器超出其容量,导致性能下降甚至区域不可用,这也会影响同一区域的其他区域,导致主机无法处理来自其他区域的请求。 以下是 Roekey 的一些设计原则:
1.长度原则
在 HBase 中,要访问一个单元格,需要有行键、列和列名,如果行键和列名太大,会占用大量的内存空间,所以行键和列的长度应该尽可能短。 rowkey 的最大长度为 64kb,建议越短越好,长度为 10-100,最好是 8 的整数倍。
另外:如果 rowkey 是数值类型,建议使用 long 类型,因为 long 类型是 8 个字节,8 个字节可以容纳非常大的无符号整数,例如:18446744073709551615。 如果是字符串,则存储为每个字符一个字节,这需要三倍的字节存储速度。
2.散列原理
如果 rowkey 是按照时间戳递增的,不要把时间放在二进制代码的前面,建议用 rowkey 的高位作为程序随机生成的 hash 字段,把时间字段放在低位,这样会提高数据均匀分布在各个 regionserver 的概率,实现负载均衡。 如果没有哈希字段,则第一个字段直接是时间信息,所有数据都会集中在一个 regionserver 上,这样在检索数据时负载会集中在特定的 regionserver 上,造成热点问题,降低查询效率。
3.唯一的原则
在设计 rowkey 时,必须保证 rowkey 的唯一性,因为 HBase 中的数据存储是 key-value 的形式,如果将相同的 rowkey 数据插入到 HBase 的同一张表中,则现有数据会被新数据覆盖。 而且,行键是按字典顺序排序和存储的,所以在设计行键时,要充分利用这种排序的特点,将经常读取的数据存储到一块中,把最近可能访问的数据放在一块中。
4.分选原理
HBase 会按照 ASCII 的自然顺序对行键进行排序,所以反过来,我们在设计行键时,可以根据这个特征设计出完美的行键,利用好这个特性就是排序原则。
二、常用设计方案
1.反转密钥
反转固定宽度的行键或数字行键,使变化最频繁的部分(最低有效位)在前面,反转分为一般数据反转和时间戳反转,其中时间戳反转更为常见。
适用场景:比如我们最初设计的rowkey在数据分布上是不均匀的,但是rowkey尾部的数据表现出良好的随机性(注意:强随机性意味着它变化频繁,没有意义,但分布是好的),这时可以考虑翻转rowkey的信息,或者直接将尾部的字节推进到rowkey的开头。 反转可以有效地使行键随机分布,但反转后肯定不能保证顺序,因此牺牲了行键的有序性。
缺点:它适用于get操作,但不适用于扫描操作,因为原始行键上数据的自然顺序已被打乱。
2. slat
SLAT 为每个 rowkey 添加一个前缀,前缀使用一些随机字符使数据分散在多个不同的区域,以达到区域负载均衡的目的。 由于这种分配是随机的,因此如果您希望按字典顺序检索行,则需要做更多的工作,这样,板条会增加写入的吞吐量,但会以读取为代价。
适用场景:例如,我们设计的 rowkey 是有意义的,但数据相似,随机性相对较低,并且无法通过反转来保证随机性,因此无法根据rowkey将其分配到不同的区域。
需要注意的是,随机数应该能够确保数据在所有区域之间进行负载均衡,这意味着分配的随机前缀数应与要将数据分发到的区域数相同。 只有这样,才会根据随机生成的前缀将加盐的行键分发到每个区域,避免热点。
缺点:因为添加的随机数,如果添加后还是基于原来的rowkey查询,是不可能知道随机数是什么的,所以在查询的时候需要在每个可能的区域找到它,加盐不利于读取。 此外,加盐会增加读取和写入过程中的吞吐量。
3.散 列
使用哈希哈希代替随机盐前缀的优点是给定的行具有相同的前缀,这不仅分散了区域的负载,而且缩短了读取操作,并且确定性哈希(例如在 md5 之后取前 4 个左前缀)允许客户端重建完整的 rowkey,并直接使用 get 操作来获取所需的行。 数据量越大,分区越均衡,如果 rowkey 是数值类型,也可以考虑 mod 方法。
适用场景:其实哈希和加盐的适用场景是差不多的,但是因为加盐方法的前缀是随机数,不方便用原来的rowkey查询,所以出现了哈希方法,因为哈希是各种常用算法计算出的前缀,所以哈希不仅可以将负载分摊到整个集群, 但也可以轻松读取数据。
缺点:与反转类似,哈希也会破坏行键的自然顺序,因此不利于扫描。
3. 总结
本文阐述了HBase的热点数据问题,主要由RowKey设计不合理导致,基本设计原则包括RowKey的长度、哈希、唯一性和排序,常见的几种设计方案包括板条(拼接)、哈希、密钥反转等。
对于这些行键设计方案,单一的设计有时并不能有效解决数据热点问题,需要结合实际情况综合运用多种方案,所以程序员遇到问题后,需要多做更多的测试,找到适合自己问题的方案。