Rowkey设计、索引的使用、多条件查询
(一) 行键rowkey设计
如何在HBase中存储自己的数据,以及如何设计表结构,HBase中表分为高表和宽表两类,前者指列少而行多,后者正好相反。因为HBase的查询主要依赖rowkey,因此应该尽量将需要查询的维度或者信息放在rowkey中,因为通过rowkey去筛选记录的效率最高。
用户可能需要扫描一定范围的记录,此时可以通过设置起始键和结束键,如果只需要起始键,只需要将终止键设置成相同的键并在末尾加上一些数据,如用户可以把aaaa设为起始键,终止键设为aaaab,此时选取的就是aaaa;
时间序列的表设计:当处理流式数据的时候,最常见的数据就是按照时间序列存储数据,这样的数据有一个特点就是这些数据会有序的存储在一个特定的范围内,由于一个region只能由一个服务器管理,所有的更新插入都会集中在一台服务器上,这样会产生读写热点,造成数据过分集中导致整个系统性能下降,解决这种问题通常的方法是:
-
salting方式
用户可以使用salting前缀来保证数据分散到所有region,如下所示:
prefix =(byte)(Long.hashCode(timestamp)%);
rowkey=Bytes.add(Bytes.toByte(prefix),Bytes.toBytes(timestamp);
这种方法是先用一个时间相关的数与region数进行取余计算,然后获得结果与rowkey进行拼接;这样数据就平均的分散到了所有的region上;这种情况存在一个弊端就是当用户要连续查询的时候,需要对每个region服务器都发起请求,不利于用户进行scan选取连续的键值。
-
字段交换
用户的rowkey包含多个属性字段,可以将随机性较大的字段放在首位,如果用户的rowkey只包含时间戳,则应当将其他字段从列或者value中提取出来,放在rowkey前段。 -
随机化
随机化是将rowkey完全随机化,比如MD5计算值,这种方法只适合用户不需要连续的扫描数据只需要随机查询的的情况。
我们在设计rowkey的时候需要根据自己的在读写性能之间寻找一个平衡点。
HBase的几个设计原则:
- rowkey
rowkey是HBase的key-value存储中的key,通常使用用户要查询的字段组合起来作为rowkey。可以通过rowkey的设计来满足不同的查询场景:
A. 数字rowkey的从大到小的排序:原生HBase只支持从小到大的排序,这样对于排行榜一类的查询需求很尴尬,可以采用rowkey=integer.MAX_VALUE-rowkey的当时进行转换,在应用层再转回来即可完成查询需求;
B. rowkey的散列原则:如果rowkey是类似时间戳的方式,可以用reverse的方式反转rowkey,使得rowkey大致均衡分布,这样可以使得region的负载均衡。 - Columnfamily
columnfamily尽量少,原因是过多的columnfamily之间会相互影响。 - Column
对于column需要扩展的应用,column可以按照普通的方式设计,但对于列相对固定的应用,最好采用将一行记录封装到一个column中的方式,这样能够节省存储空间。
(二) 索引的使用
HBase没有原生的辅助索引进行支持,但是有些场景仍然需要用到辅助索引,通常的需求是用户需要根据主坐标(rowkey、列族、列名)来查询一个单元格,也可以通过一个其他类型的坐标来完成索引,下面是几种解决方案:
- 带索引的事务型HBase
- 带索引的IHbase
- 协处理器
- 客户端维护索引
前面两种都是对于HBase的开源扩展形态,都需要额外的JAR文件和配置来支持,而且对于最新版的HBase支持性较差,协处理器是HBase的一个重要特性,可在协处理器介绍中查阅。下面主要介绍客户端维护索引:
在客户端管理索引的一个典型的做法是维护一个数据表和一个(多个)查找/映射表,每当数据更新时,同时也更新查找/映射表,查找/映射表中存储的是数据表记录的rowkey,查找数据时可以直接在数据表中查找,也可以根据查找的条件从查找/映射表中查找数据表的rowkey,然后根据rowkey查找数据。
这种做法的优点是整个逻辑都有客户端维护处理,用户可以定制自己的映射关系,缺点是原子性较差,用户不能保证数据表和查找/映射表的一致性。
(三) 多条件查询
索引的一个基本应用时多条件查询,例如每个用户每天产生的消息用如下rowkey表示:uid-date-MsgId
我们查询一个用户所有天的消息记录可以匹配uid-,这样设置起始行键查询效率很快,但是,当我们需要查询一天所有用户的消息记录的话就需要类似这样的匹配-date-*,这样查找的话就是全表扫描,是很不效率的。因此可以维护一个查找/映射表,表中rowkey存为:date-uid-MsgId,单元格为对应rowkey在数据表中的rowkey,这样查询一天所有的用户则可以先从查找/映射表中找一天所有用户的rowkey,然后根据这些rowkey去数据表中查找真实的记录。