--==========================================================================
---1. get 获取table中rowkey语句 于 Scan获取table中rowkey语句
--【get】
hbase(main):011:0> get 'liupeng:employee','1001'
COLUMN CELL
contect:mail timestamp=1522202414649, value=liupliup@cn.ibm.com
contect:phone timestamp=1522202430196, value=15962459503
group:number timestamp=1522202455929, value=1
info:age timestamp=1522202371257, value=34
info:name timestamp=1522202364156, value=liupeng
--【Scan】
hbase(main):010:0> scan 'liupeng:employee',FILTER=>"PrefixFilter('1001')"
ROW COLUMN+CELL
1001 column=contect:mail, timestamp=1522202414649, value=liupliup@cn.ibm.com
1001 column=contect:phone, timestamp=1522202430196, value=15962459503
1001 column=group:number, timestamp=1522202455929, value=1
1001 column=info:age, timestamp=1522202371257, value=34
1001 column=info:name, timestamp=1522202364156, value=liupeng
1 row(s) in 0.0590 seconds
---总结:
--Scan的结果包含了rowkey本身。而get获取到的信息不包含rowkey的值。
--另外,get的column于cell是分开的。而Scan是2者结合在一起的,Scan中FILTER过滤“PrefixFilter”关键字是用来筛选rowkey的。
--==========================================================================
--2. get于Scan获取table中单条数据信息中的区别
--把table中rowkey为1001,元素为'159'的信息查询出来
COLUMN CELL
info:phone timestamp=1527914569028, value=15962459503
1 row(s) in 0.0320 seconds
hbase(main):230:0> scan "liupeng:employee",FILTER=>"PrefixFilter('1001')AND ValueFilter(=,'substring:159')"
ROW COLUMN+CELL
1001 column=info:phone, timestamp=1527914569028, value=15962459503
1 row(s) in 0.1010 seconds
--==========================================================================
--3. Scan模糊查询直接找到了只要包含159的字段的值就全部显示出来。
--而get的语法如下所视必须指定rowkey的基础上才可以查询columns。
--这就需要对rowkey定义的时候,考虑全面的涉及才可以做到。因此从这点来看Scan的方法个人认为比get获取信息更加的便捷。
hbase(main):026:0> scan 'liupeng:employee',FILTER=>"ValueFilter(=,'substring:159')"
ROW COLUMN+CELL
1001 column=contect:phone, timestamp=1522202430196, value=15962459503
1002 column=contect:phone, timestamp=1522202527866, value=15977634464
--==========================================================================
-- 4.Scan方法可以不用指定rowkey检索的情况下直接找valuse值。
--更具体点说也就是我们要找的哪个column中的哪个value值。get方法是无法做到这一点的。
--ColumnPrefixFilter('列名')
hbase(main):038:0> scan 'liupeng:employee',FILTER=>"ColumnPrefixFilter('name')"
ROW COLUMN+CELL
1001 column=info:name, timestamp=1522202364156, value=liupeng
1002 column=info:name, timestamp=1522202474669, value=Jack_Ma
1003 column=info:name, timestamp=1522202561029, value=kevin_shi
3 row(s) in 0.0210 seconds
##注释:ColumnPrefixFilter代表指定具体哪一个column(key(info)对应的value(name))。
--==========================================================================
--5. Scan方法方便在于它可以随意指定rowkey,column以及value的值来进行查找。
--还可以结合AND,ORD等条件语句并用来找到自己想要的数据。
hbase(main):060:0> scan 'liupeng:employee',FILTER=>"ColumnPrefixFilter('ph') AND ValueFilter(=,'substring:15962')OR ValueFilter(=,'substring:186')"
ROW COLUMN+CELL
1001 column=contect:phone, timestamp=1522202430196, value=15962459503
1003 column=contect:phone, timestamp=1522202605976, value=18665851263
2 row(s) in 0.0170 seconds
--==========================================================================
--6. 通过SingleColumnValueFilter类方法指定检索值列举出检索值对应的所有列及value数据
-- SingleColumnValueFilter类还提供正则表达式查询方法。可以通过模糊查询来查找对应的rowkeys,columns以及values。
hbase(main):244:0> scan "liupeng:employee",{FILTER=>"SingleColumnValueFilter('info','name',=,'regexstring:liu')"}
ROW COLUMN+CELL
1001 column=contect:mail, timestamp=1527231141046, value=liupliup@cn.ibm.com
1001 column=info:address, timestamp=1527753987327, value=shanghai
1001 column=info:age, timestamp=1527231097033, value=34
1001 column=info:name, timestamp=1527231081262, value=liupeng
1001 column=info:phone, timestamp=1527914569028, value=15962459503
1004 column=contect:mail, timestamp=1527473497956, value=lqdong@jingdong.com
1004 column=info:address, timestamp=1527755135174, value=shenzhen
1004 column=info:age, timestamp=1527473477124, value=40
1004 column=info:name, timestamp=1527415665182, value=liuqiangdong
2 row(s) in 0.0080 seconds
三、方舟HBase Rowkey设计实战
在实际的设计中我们可能更多的是结合多种设计方法来实现Rowkey的最优化设计,
比如设计订单状态表时使用:Rowkey: reverse(order_id) + (Long.MAX_VALUE – timestamp),
这样设计的好处一是通过reverse订单号避免Region热点,二是可以按时间倒排显示。
结合易观方舟使用HBase作为事件(事件指的的终端在APP中发生的行为,比如登录、下单等等统称事件(event))的临时存储(HBase只存储了最近10分钟的热数据)来举例:
设计event事件的Rowkey为:两位随机数Salt + eventId + Date + kafka的Offset
这样设计的好处是:
设计加盐的目的是为了增加查询的并发性,假如Salt的范围是0~n,那我们在查询的时候,可以将数据分为n个split同时做scan操作。经过我们的多次测试验证,增加并发度能够将整体的查询速度提升5~20倍以上。随后的eventId和Date是用来做范围Scan使用的。在我们的查询场景中,大部分都是指定了eventId的,因此我们把eventId放在了第二个位置上,同时呢,eventId的取值有几十个,通过Salt + eventId的方式可以保证不会形成热点。在单机部署版本中,HBase会存储所有的event数据,所以我们把date放在rowkey的第三个位置上以实现按date做scan,批量Scan性能甚至可以做到毫秒级返回。
这样的rowkey设计能够很好的支持如下几个查询场景:
1、全表scan
在这种情况下,我们仍然可以将全表数据切分成n份并发查询,从而实现查询的实时响应。
2、只按照event_id查询
3、按照event_id和date查询
此外易观方舟也使用HBase做用户画像的标签存储方案,存储每个app的用户的人口学属性和商业属性等标签信息,由于其设计的更为复杂,后续会另起篇幅详细展开。
最后我们顺带提下HBase的表设计,HBase表设计通常可以是宽表(wide table)模式,即一行包括很多列。同样的信息也可以用高表(tall table)形式存储,通常高表的性能比宽表要高出 50%以上,所以推荐大家使用高表来完成表设计。表设计时,我们也应该要考虑HBase数据库的一些特性:
1、在HBase表中是通过Rowkey的字典序来进行数据排序的
2、所有存储在HBase表中的数据都是二进制的字节
3、原子性只在行内保证,HBase不支持跨行事务
4、列族(Column Family)在表创建之前就要定义好
四、总结
在做Rowkey设计时,请先考虑业务是读比写多、还是读比写少,HBase本身是为写优化的,即便是这样,也可能会出现热点问题,而如果我们读比较多的话,除了考虑以上Rowkey设计原则外,还可以考虑HBase的Coprocessor甚至elastic search结合的方法,无论哪种方式,都建议做实际业务场景下数据的压力测试以得到最优结果。
————————————————
转载于 代立冬的文章