1.Get操作(已知精确的rowkey,查value)
比较重要的是进行散列,否则有可能大部分请求落到一个机器上
需求:根据业务ID,获取这个业务ID对应的属性
以上图为例:
为什么要进行md5 或 Hash 或进行反转?
一句话说明:负载均衡,可以将记录平均分到不同的region
比如:业务id有时候开头是跟业务相关的一些编码。有个可能某个编码下面对应记录比较多,某些编码对应比较少。
比如上图示例中1111开头的有3条数据。其他前缀的都在3条以下
所以通过md5或者其他方式进行散列。
MD5 => DHS中选十六进制(string)
手机号反转=>DHS中选十进制(string)
其他自定义hash并序列化存储的=>DHS中选二进制
api:
写入:table.put。
查询:通过rowkey精准查询:table.get( Get(rowkey) )
DHS选“十六进制String类型”
MD5开头前缀作为rowkey代码写法:
1 2 3 4 5 6 |
|
2.Scan操作(扫描多行数据操作):
phoenix表底层也是hbase表,如果不是主键完全匹配,扫多条数据的话,实现方法基本上是带salt_bucket 的scan
利用hbase表,字典排序的特性,进行的扫描操作,要查询的数据有共同的前缀
scan操作不是无限制的!目前HBase最佳场景是rowkey分散的每次1w条以下的scan。 (如果每条数据1k,一次scan 1w条,响应时间在500ms~1s之间)
适合场景:
例如,时序需求
写入用户id+时间戳对应时间的轨迹点经纬度信息,根据 具体用户id+开始时间+结束时间 查询这个用户某一段时间内的轨迹点。
用户id比较分散,每个id被查询机会比较均等,比如 某用户行为
rowkey设计: 散列(用户id-目前是手机号)+时间戳。
伪代码实现:
|
不适合场景:
一次扫描,startkey,endkey之间的数据有10000条甚至更多的场景。
例如,如果将10w条相邻的数据分成多次扫描(类似导出某一业务一年的明细数据),其实这个操作也是落到了这段相邻的数据上,造成这段数据有热点。
HBase最优场景是startkey分散的,每次扫描在1000以内的场景。Scan每次最大扫描到1w已经是比较重的操作了。再大的scan可能需要用户申请的机器数量比较多,或许其他存储会更适合。
线上业务:每次scan的数据尽量在1000条以内
scan操作一般都设置startkey,stopkey。数据量比较多的时候(比如1w以上,2w以内)要增加salt_bucket
但是这还不够:为防止超时,scan 1w条以上的操作,要分成多次进行scan。因为scan数据操作会有大量的磁盘读取操作(磁盘IO升高),把结果放入内存(占用内存导致GC频率升高),再通过RPC发送到客户端(网络带宽)。
如果设置Filter进行过滤呢?——即使设置了Filter,也会扫描startkey到stopkey之间的所有数据。再在内存中过滤之后再返回客户端。
对客户端有什么影响?如果不分批扫描,除了对服务器磁盘内存网络造成压力,也会造成客户端超时。
下面是增加salt_bucket之后,分多个线程同时scan的情况。如果没有分salt_bucket,就是其中一个scan线程的情况。
3.Phoenix表的请求会转化成scan请求,那么如何将一个大的scan拆分成多个小的scan?
phoenix主键相当于hbase的rowkey。如果查询条件中不是精确匹配所有主键,那么查询基本都是scan。
hbase的scan是通过前缀匹配进行的,
如果第一个主键未精确匹配,
那么就查全表,这种操作是禁止的。会占用大量磁盘,内存和网络
如果第一个主键匹配了(where条件中写了col01='aaaaa'),那么后面看第二个主键是否精确匹配。
第二个主键也是精确匹配(在where条件中, col02='bbbb')那么扫描的数据量是符合col1='aaaaa' and col2='bbbb'的数据量,
如果第二个主键未设置=或者< >的条件,
那么这个查询相当于要查出col01='aaaaa'的所有数据,然后到内存中过滤后面col3,col4....的数据。
例如:假设有这么一个表 :
1 2 3 4 5 6 7 8 |
|
最初设计:主键是key01
原来的需求,常用查询是下面两条sql:
select a,b,c from TABLE where key01=‘a’ and key02='b' and key03='c'
select a,b,c from TABLE where key01=‘a’ and key02='b'
实际执行会按salt_bucket设计scan扫描出key01的所有数据(salt_bucket在phoenix自己封装了放到rowkey前缀上),放到内存里再判断key02,key03,col01,col02的值
所以phoenix主键要将常用查询字段都放进去,并且靠前面的字段要能尽量过滤出较少的数据
如果where条件中没有主键字段,那么会扫描全表,这样的操作是不允许的。