RowKey的设计原则

RowKey在读写中起到的作用

  • 读写时通过RowKey路由到对应的Region;
  • MemStore中的数据按RowKey进行排序;
  • HFile中的数据按RowKey进行排序;
    RowKey的设计直接影响Region的划分

Hbase通过RowKey检索数据的三种方式

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

RowKey设计的目标

  • 结合业务特点,将数据合理的分配到每一个Region中,从而很好的满足业务的读写需求;

索引设计的目标

  • 为Hbase提供更多维度查询的能力,在实际应用中应该通过构建尽量少的索引,来满足更多的查询场景;

影响查询的关键因素

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

遵循的最基本原则

  • 唯一性:RowKey必须能够唯一的识别一行数据;无论应用时什么样的负载特点,RowKey字段都应该参考最高频的查询场景。数据库通常都是以如何高效的读取和消费数据为目的,而不是存储本身。而后,结合具体的负载特点,再对选取的RowKey字段值进行改造,组合字段场景下需要重点考虑字段的顺序。
  • RowKey 长度原则:RowKey 是一个二进制码流,可以是任意字符串,最大长度 64kb ,实际应用中一般为 10-100bytes,以 byte[] 形式保存,一般设计成定长。建议越短越好,不要超过 16 个字节。列族、列名的命名在保证可读的情况下也应尽量短。value 永远和它的 key 一起传输的。

  • 排序原则:RowKey 是按照字典顺序排序存储的,因此,设计 RowKey 的时候,要充分利用这个排序的特点,将经常读取的数据存储到一块,将最近可能会被访问的数据放到一块。一个常见的数据处理问题是快速获取数据的最近版本,使用反转的时间戳作为 RowKey 的一部分对这个问题十分有用,可以用 Long.Max_Value-timestamp追加到key的末尾。例如 [key][reverse_timestamp] , [key]的最新值可以通过scan [key]获得[key]的第一条记录,因为 HBase 中 RowKey 是有序的,第一条记录是最后录入的数据。

避免数据热点的方法-Reversing

  • 如果经过设计的RowKey在数分布不均匀,但是RowKey尾部的数据却呈现了良好的随机性。此时,可以考虑将RowKey的信息反转,或者直接将尾部的bytes提前到RowKey的前部。
  • 缺点:场景利于Get但不利于Scan,因为数据在原RowKey上的自然顺序已经被打乱。
  • 手机号的前缀变化比较少(如152、185等),但后半部分变化很多。如果将它反转过来,可以有效地避免热点。不过其缺点就是失去了有序性。

避免数据热点的方法--Salting

  • Salting的原理是在原RowKey的前面添加固定长度的随机bytes,随机bytes能保障数据在所有Regions间的负载均衡。
  • 注意的是分配的随机前缀的种类数量应该和我们想把数据分散到的那些 region 的数量一致。只有这样,加盐之后的 rowkey 才会根据随机生成的前缀分散到各个 region 中,避免了热点现象。
  • 缺点:既然是随机bytes,基于原RowKey查询时无法获知随机bytes信息是什么,也就是需要去各个可能的Regions中去查看。可见,Salting对于读取是利空的。

避免数据热点的方法-Hashing

  • 基于RowKey的完整或部分数据进行Hash,而将Hashing后的值完整替换原RowKey或者部分替换RowKey的前缀部分;
  • 当 Rowkey 是按时间戳的方式递增,就不要将时间放在二进制码的前面,可以将 Rowkey 的高位作为散列字段,由程序循环生成,可以在低位放时间字段,这样就可以提高数据均衡分布在每个 Regionserver 实现负载均衡的几率。
  • 缺点:与reversing类似,Hashing也不利于Scan,因为打乱了原RowKey的自然顺序。

  实际业务中,有一部分是用户在日历上记录自己的行为。需要储存在 RowKey 中的维度有:用户 ID(uuid,不会超过十亿)、日历上的日期(date,yyyyMMdd 格式)、记录行为的类型(type,0~99 之间)。记录的详细数据则存储在列 f:data 中。根据查询逻辑,可以设计的 RowKey 格式如下:

9~79809782~05~0008839540

  各个部分的含义如下:

uuid.toString().hashCode() % 10

99999999 - date

StringUtils.leftPad(type, 2, "0")

StringUtils.leftPad(uuid, 10, "0")

  

  

posted on 2022-05-30 08:54  溪水静幽  阅读(555)  评论(0)    收藏  举报