Hbase Rowkey设计-总结与举个栗子

Hbase Rowkey设计

想要设计一个合适的Rowkey,首先需要了解Hbase通过RowKey检索数据的三种方式:

  1. 通过单个Row key访问:即按照某个Row key键值进行get操作;
  2. 通过Row key的range进行scan:即通过设置startRowKey和endRowKey,在这个范围内进行扫描;
  3. 全表扫描:即直接扫描整张表中所有行记录。

那么接下来就需要考虑影响查询的关键因素有哪些:

  • 基于某一个索引/RowKey进行查询时,影响查询的最关键因素在于能否将扫描的候选结果集限定在一个合理的范围内。
  • 如何合理的设置查询驱动条件。什么是查询驱动条件与查询过滤条件?直接影响数据扫描范围的查询条件,称之为查询驱动条件。而其他的能够起到过滤作用的查询条件,称之为查询过滤条件。

设计Rowkey主要考虑两方面的内容:

  • 查询效率
  • 避免热点

接着就需要调研具体使用场景,可以从以下三方面开始:

  1. 查询场景,如:
    1. 需要支持哪些查询场景?时延要求?
    2. 最高频的查询场景是什么?
    3. 最有价值的数据排序场景是什么?
    4. 是否有其他维度的价值查询场景?维度?
    5. 是否是组合字段场景?
    6. 各个字段的匹配类型?。
  2. 负载特点,如:
    1. 读写TPS?读写比重:重写轻读?重读轻写?读写相当?
    2. 数据负载均衡与高效读取时常时矛盾的。
    3. 在重读轻写的大数据场景重,RowKey设计应该更侧重于如何高效读取;
    4. 而在重写轻读的大数据场景重,在满足基本查询需求的前提下,应该更关注整体的吞吐量,这就对数据的负载均衡提出了很高的要求;
  3. 数据特点,如:
    1. 查询条件字段的离散度信息?字段离散度的定义:字段A的离散度=(字段A的可能的枚举值数据)/数据总记录条数
    2. 查询条件字段的数据分布特点?数据分布影响RowKey的设计,更进一步影响如何查询合理的划分Region信息
    3. 数据生命周期?影响到一个表的一次Major Compaction发生时涉及到的最大数据量

三个设计原则

所以根据表的具体情况可以总结出以下三个要点:

  1. 长度原则
    Rowkey是一个二进制码流,Rowkey的长度建议设计在10到64字节,但最好不要超过16字节。
    原因如下:
    (1)数据的持久化文件HFile中是按照KeyValue存储的,如果Rowkey过长比如100个字节,1000万列数据光Rowkey就要占用100*1000万=10亿个字节,将近1G数据,这会极大影响Hfile的存储效率;
    (2)MemStore将缓存部分数据到内存,如果Rowkey字段过长内存的有效利用率降低,系统将无法缓存更多的数据,这会降低检索效率,因此Rowkey的字节长度越短越好。
    (3)操作系统一般都是64位系统,内存8字节对齐,空值在16个字节,8字节的整数倍利用操作系统的最佳特性。

  2. 唯一原则
    必须在设计上保证其唯一性,Rowkey是按照字典顺序排序存储的,因此,设计Rowkey的时候,要充分利用这个排序的特点,将经常读取的数据存储到一块,将最近可能会被访问的数据放到一块。但是这里的量不能太大,如果太大需要拆分到多个节点上去。

  3. 散列原则
    如果Rowkey是按时间戳的方式递增,不要将时间放在二进制码的前面,建议将Rowkey的高位作为散列字段,由程序循环生成,低位放时间字段,这样将提高数据均衡分布在每个Regionserver实现负载均衡的几率。
    具体方式:

    1. 加盐
      在Rowkey的前面增加随机数,具体就是给Rowkey分配一个随机前缀以使得它和之前的Rowkey的开头不同。分配的前缀种类数量应该和你想使用数据分散到不同的Region的数量一致。散列之后的Rowkey就会根据随机生成的前缀分散到各个Region上,以避免热点。
    2. 哈希
      哈希会使同一类值永远用一个前缀散列。哈希可以使负载分散到整个集群。与加盐的不同点就是,可以利用到Hbase根据字典顺序排序的这一特点,将同一类值的数据,存入同一Region当中。
    3. 预分区
      预分区就是预先创建Hbase表分区。需要我们明确Rowkey的取值范围和构成逻辑。比如举个电话详单表的栗子。通过加盐我们得到的 Rowkey构成是:随机数+主叫+被叫+时间,如果我们现在并没有500台机器,只有10台,但是按照我们的计划,未来将扩展到500台的规模。所以我们仍然设计0到499的随机数,但是将以主叫177开头的通话记录分配到十个region当中,所以我们将随机数均分成十个区域,每个区域50个单位。然后我们将我们的预分区存入数组当中,当插入数据时,先根据插入数据的首部随机数,判断分区位置,再进行插入数据。同样,这样也能使得各台节点负载均衡。
    4. 反转
      反转的类型总结有两类:
      • 对于以手机号码这样比较固定开头的Rowkey(例如开头187),但是它的后几位都是随机的,没有规律的。我们可以将手机号反转之后作为Rowkey,这样就避免了热点问题。
      • 对于时间戳字段,实际场景可能是快速获取数据的最近版本,那么使用反转的时间戳作为Rowkey的一部分对这个问题十分有用。可以用Long.Max_Value-timestamp追加到key的末尾。例如 [key][reverse_timestamp] , [key]的最新值可以通过scan[key]获得[key]的第一条记录,因为HBase中Rowkey是有序的,第一条记录是最后录入的数据。

经验之谈

  1. Rowkey既想要能够快速检索,就想要内容最好集中到少量的Region中,但是一旦集中了,就会产生热点问题,所以,他们是相伴相生。
  2. 需要查询的字段整合到Rowkey,提高查询速度。

举个栗子

设计一张数据仓库的表,满足以下需求:

  1. 存放3年的数据
  2. 3年前的数据要能高效方便的根据 (数据日期)按天删除或迁出本表。
  3. 能高效方便的让用户根据(数据日期)按天查询表中的数据
  4. 能高效方便的让用户根据(数据日期)按天追加增量数据至本表中。

Rowkey的设计方案:【随机值(加盐|哈希)】【时间日期】【反转时间戳】

优点:由于Rowkey首部数字随机,所以数据也将随机分布到不同的regionServer中。这样就能很好的避免热点问题;将时间日期加入其中,可以使可能会被访问的数据放到一块,方便查询,删除。将反转时间戳加入其中,可以使最新值可以通过scan[时间日期]获得[时间日期]的第一条记录,因为HBase中Rowkey是有序的,第一条记录是最后录入的数据。

一般的可以按照此规则设计:
设计规则: MD5 +主维度+维度标识+子维度 +时间维度+
子维度2

举个栗子:卖家 ID MD5 前四位+卖家 ID+ app 级类目 ID+
ddd +二级类目 ID

以MD5 前四位作为 row key 的第一部分,可以把数据散列,让服
务器整体负载是均衡的,避免热点问题。在上面的例子中,卖家 ID
于主维度 ,在查数据时是必传的。每个统计维度都会生成一个维度标识,以便在 rowkey 上做区分。

参考文章:https://www.cnblogs.com/zmoumou/p/10292676.html

猜你喜欢

转载自blog.csdn.net/weixin_42526352/article/details/105736969