【HBase使用】HBase表设计图解

 

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-目前是手机号)+时间戳。

伪代码实现:

table=connection.getTable

Scan scan=new Scan();

scan.setStartkey("用户A_id_开始时间");

scan.setEndkey("用户A_id_结束时间");

Result result=table.getScanner(scan);

遍历result

 不适合场景:

一次扫描,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

CREATE TABLE TEST_ACL_YJY.TEST_C_01

(key01 BIGINT NOT NULL, //主键01

key02 BIGINT ,

key01 INTEGER ,

col01 VARCHAR, //业务字段1

col02 VARCHAR, //业务字段2

col03 VARCHAR, //业务字段3

CONSTRAINT ID_PK PRIMARY KEY (key01))  DATA_BLOCK_ENCODING = 'FAST_DIFF',COMPRESSION = 'SNAPPY',COLUMN_ENCODED_BYTES = NONE, UPDATE_CACHE_FREQUENCY=300000,SALT_BUCKETS = 1,TTL=86400;

最初设计:主键是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条件中没有主键字段,那么会扫描全表,这样的操作是不允许的。

 

 

 

 

 

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值