RowKey是什么?
HBase中RowKey可以唯一标识一行记录,在HBase查询时会有几种形式:
1 通过get方式,指定RowKey获取唯一一条记录。
2 通过scan方式,设置startRow和stopRow参数进行范围匹配。
3 全表扫描,直接扫描整表所有数据。
RowKey字面上来看,就是行键意思,在增删改查中充当主键,它可以使任意字符串,在HBase内部RowKey保存为字节数组。
HBase中的数据是按照RowKey的ASCII字典顺序进行全局排序的,因此在设计RowKey时,要利用排序存储的特性,将经常读取的行存储到一起,避免做全表扫描。
Rowkey设计原则
- HBase由于其存储和读写的高性能,在OLAP及时分析中发挥重要作用,HBase的查询只能通过rowkey来查询(rowkey便表示唯 一 一 行记录)
- rowkey设计的优劣直接影响读写性能。HBase中的数据是按照rowkey的ASCII字典书序来进行全局排序
- HBase提出的设计原则主要有:长度原则,唯一原则,排序原则和散列原则。
举例 :
假如有5个RowKey:”012“,"0","123","234","3",按ASCII字典排序后的结果为:"0", "012", "123", "234", "3”
Rowkey排序时会先对比连个Rowkey的第一个字节,结果相同;然后会对比第二个字节,依次类推
由于HBase是通过Rowkey查询的,一般Rowkey都会存一些比较关键的检索信息,查询时会根据查询方式进行数据存储格式的设计,应该避免做去全表查询
长度原则
- RowKey是一个二进制码流,可以是任意字符串,最大长度为64kb,实际应用中一般为10-100bytes,以byte[]形式保存,一般设计成定长,建议越短越好,不要超过十六个字节,原因是:
- 在 HBase 的底层存储 HFile 中,RowKey 是 KeyValue 结构中的一个域。假设 RowKey 长度 100B,那么 1000 万条数据中,光 RowKey 就占用掉 100*1000w=10亿个字节 将近 1G 空间,这样会极大影响 HFile 的存储效率。
- HBase 中设计有 MemStore 和 BlockCache,分别对应列族/Store 级别的写入缓存,和 RegionServer 级别的读取缓存。如果 RowKey 字段过长,内存的有效利用率就会降低,系统不能缓存更多的数据,这样会降低检索效率。
- 此外,目前使用的基于64位的操作系统,内存是按照8B对齐的,所以设计RowKey时一般做成8B的整数倍,如16B或者24B,可以提高寻址效率。
- 同样,列族、列名的命名在保证可读的情况下也应尽量短。value 永远和它的 key 一起传输的。当具体的值在系统间传输时,它的 RowKey,列名,时间戳也会一起传输(因此实际上列族命名几乎都用一个字母,比如‘c’或‘f’)。如果RowKey和列名和值相比较较大,Hfile中的索引最终占据了HBase分配的大量内存。
、
唯一原则
- 由于RowKey用来唯一标识一行记录,所以必须在设计上保证RowKey的唯一性。
- 由于 HBase 中数据存储的格式是 Key-Value 对格式,所以如果向 HBase 中同一张表插入相同 RowKey 的数据,则原先存在的数据会被新的数据给覆盖掉(和 HashMap 效果相同)。
排序原则
- RowKey是按照字典顺序排序存储的,所以设计RowKey时,利用排序特性,将经常读取的数据存储到一起,将最近可能访问的数据放到一起。
- 一个常见的数据处理问题是快速获取数据的最近版本,使用反转的时间戳作为 RowKey 的一部分对这个问题十分有用,可以用 Long.Max_Value-timestamp追加到key的末尾。
- 例如 [key][reverse_timestamp] , [key]的最新值可以通过scan [key]获得[key]的第一条记录,因为 HBase 中 RowKey 是有序的,第一条记录是最后录入的数据。
散列原则
- 散列原则就是设计出来的RowKey需要能均匀的分布到各个RegionServer上。
- 比如设计RowKey时,当RowKey是按时间戳的方式递增,就不要将时间放在二进制码的前面,可以将RowKey的高位作为散列字段,由程序循环生成,可以在低位放时间字段,这样就可以提高数据均衡分布在每个RegionServer实现负载均衡的几率。
- 如果没有散列字段,首字段只有时间信息,那就会出现所有新数据都在一个 RegionServer 上堆积的热点现象,这样在做数据检索的时候负载将会集中在个别 RegionServer 上,降低查询效率。
数据热点问题
- 不合理的RowKey设计产生热点问题,热点发生在大量的client直接访问集中在个别RegionServer上(访问可能是读,写或者其他操作),导致单个RegionServer机器自身负载过高,引起性能下降
- 大量的访问使热点region所在的单个机器超出自身承受能力,引起性能下降甚至导致region不可用,也将影响到同一个RegionServer上的其他region,由于主机无法服务其他region请求,就造成数据热点的现象。
- 所以在向HBase中插入数据时,应该优化RowKey的设计,使数据被写入集群的多个region中,尽量将记录均衡的分散到不同的region中,平衡每个region的压力。
解决数据热点问题的方法
- 主要的方法有反转,加盐和哈希
解决办法solution:
1、reverse反转
例子:手机号反转后的字符串作为rowkey,这样避免了以手机号开头比较固定引起的热点问题 (如152、185等),但后半部分变化很多 如果将它反转过来,可以有效地避免热点
缺点:牺牲了rowkey的有序性
反转时间这个操作严格来讲不算“打散”,但可以调整数据的时间排序。如果将时间按照字典序排列,最近产生的数据会排在旧数据后面。如果用一个大值减去时间(比如用99999999减去yyyyMMdd,或者Long.MAX_VALUE减去时间戳),最新的数据就可以排在前面了。
2、salt加盐
在RowKey前添加一些前缀,加盐的前缀种类越多,RowKey被打的越散。
比如在一个有4个Region的HBase表中,加salt前的rowkey:abc001,abc002,abc003,分别加上a、b、c前缀后的rowkey为a-abc001、b-abc002、c-abc003;可以看到,salting前数据分布在两个rowkey里,salting后数据分布在三个rowkey钟,避免热点现象 ,增加了读写的吞吐量
缺点:增加了读写开销
3.Hash散列或者Mod
例子:将上述的rowkey经过hash处理,采用md5散列算法取前4位做前缀
结果:
9bf0-abc001 (abc001在md5后是9bf049097142c168c38a94c626eddf3d,取前4位是9bf0)
7006-abc002
95e6-abc003
上面几个rowkey数据会分布在3个region中,实际应用场景中,这种设计会使得分区之间更加均衡
总结
- 在做Rowkey设计时,请先考虑业务是读比写多、还是读比写少,HBase本身是为写优化的,即便是这样,也可能会出现热点问题,而如 果我们读比较多的话,除了考虑以上Rowkey设计原则外,还可以考虑HBase的Coprocessor甚至elastic search结合的方法,无论哪种方 式,都建议做实际业务场景下数据的压力测试以得到最优结果。