一、查询最左匹配原则
假设查询包含3个维度:uid,city,age, 将rowkey的格式为: uid_city_age 则以下维度的查询都会比较高效
- 通过uid查询
- 通过uid+city查询
- 通过uid、city和age查询
而以下维度的查询则会相对低效一些而以下维度的查询则会相对低效一些
- 通过city查询
- 通过age查询
- 通过city + age查询
- 通过uid + age查询
其中1、2、3将会是全表扫描操作,因此非常低效,但是如果有这样的查询需求怎么办?可以考虑引入phoenix二级索引。
二、避免热点Region出现
热点Region容易导致读写出现性能瓶颈,因此通常需要对原始rowkey做一些特殊处理,来保证数据写入能够均匀散列
通常的做法有以下几种:
-
将rowkey进行反转(适用于自增性质的rowkey)
比如订单id这个字段是每次加1自动增长的,如果直接将其作为rowkey来进行写入将会产生写入热点(相邻rowkey全部写入到了一个Region里),而反转之后的写入是可以散列分布到每一个Region的。
针对反转操作,hbase客户端SDK提供了com.meituan.service.hbase.HBaseUtil#reverse实用方法来供业务调用使用 -
在原始rowkey之前添加一个hash前缀
适用于以下业务场景
(1). 原始rowkey的前缀都比较相似,很难通过前缀来合理划分区间
(2). rowkey的前缀区间不固定或很难确定,导致没有办法做预定义分区处理
(3). 同样适用于自增性质的rowkey
- 对表格进行加盐处理
适用场景:rowkey包含时间戳信息,比如格式为pigeId_timestamp,并且有个别pigeId的吞吐非常庞大,单Region已经无法承载相应的写入需求。
此时需要对原始rowkey进行加盐处理,将同一个pigeId的数据分散到不同的Region中进行写入。
加盐处理与加hash前缀的处理方式比较类似,都是在原始rowkey之前加一个散列前缀,但是应用场景却不相同。
拿格式为pigeId_timestamp的rowkey举例,如果单个pigeId的吞吐不大,单Region能够满足吞吐需求,那么只需要针对pigeId进行hash即可,
将rowkey转换成hash(pigeId)_pigeId_timestamp格式,即方案2的处理方式。
而如果单Region满足不了个别pigeId的吞吐需求则需要对pigeId_timestamp整体进行hash处理,将rowkey转换成hash(pigeId_timestamp)_pigeId_timestamp格式。
不同格式的rowkey在查询处理上会有以下不同,比如想要查询某个pigeId在指定时间范围内的数据记录。
(1) 如果格式为hash(pigeId)_pigeId_timestamp,只需要扫描个别Region即可,因为相同时间范围内的记录是顺序写入到同一Region的
(2) 而如果格式为hash(pigeId_timestamp)_pigeId_timestamp,则需要对全部Region进行扫描,因为相同时间范围内的记录是打散到每一个Region上的
针对该类型的查询请求,hbase的SDK提供了parScan方法,会并行扫描每一个Region,然后将查询结果进行聚合返回给客户端。
目前绝大部分业务采用方案2即可避免热点问题,当吞吐量变的十分巨大时建议考虑方案3