hbase 服务化文档
hbase 单独抽出平台 说白了 就是解决 hbase put ,scan 操作 ,开发比较吃力 ,不优雅等问题。
要做的事情
1 . hbase 方法 做好 告警
2 . hbase 连接池设置
3 . hbase 连接超时时间配置等
4 . 如果能提供hue那样的脚本查询 那就好了 (这能解决很多问题)
5 . 提供业务同学查询能力 ,完全屏蔽hbase 操作 不需要让他们接触 Scan 创建 ,find 方法的调用等等
6. 如果 基于sql 甚至可以做到 es ,hbase , mysql , 统一数据访问平台 ,基于配置中心 ,sql , 业务只需要传 对应的模板id ,参数 , 便能统一返回 。这种方式 优点 很多 ,数据访问 的审核(delete ,drop ,不能update别人业务模块数据 ) ,sql 性能审核和调优 ,数据库的隔离 (业务是对于数据库不知情的),数据迁移等问题 。 这种情况 我推荐 互联网的公司 比较合适 ,他们迭代周期短 ,需求变化频繁,读写并发 要求高 ,基于模板的方式 ,能够快速变更业务 ,甚至连 mybatis 都不用接触 。 缺点就是 需要有能力的人 维护 , 需要有能力的人开发 , 。对于项目型 的公司 通过mybatis 访问 phoenix + hbase 也是 一种好的方法 。
下面 主要 两个方式 , 一个基于 接口 方向 , 一个 基于 sql 方向
基于 定义接口方式 比如像 getListByPrefixRowkey
问题1
本身 filter 还是挺多的 为每个filter 方法 ,创建一个find 方法,那显然是累死人的做法 ,业务同学估计也不爽
image.png
image.png
问题2
filter list 形式 , filter 那么多 组合的形式 更是多
针对上面两个问题 mop-hbase-template 有一定的解决
但是 基本是针对ValueFilter 来做的
但是 项目实际 情况 有很多都是
RowFilter + RowFilter
image.png
RowFilter + PrefixFilter
image.png
PrefixFilter + PageFilter
image.png
ColumnPrefixFilter
image.png
所以这一块 需要单独的设计 ,
设计方案1 .简单方法为上面每个例子提供方法
比如
RowFilter + RowFilter
getListByRowFilters 同理
PrefixFilter + PageFilter
getListByRowFilterAndPageFilter
RowFilter 需要的参数 需要封装 SubstringComparator ,RegexStringComparator,BinaryPrefixComparator 以及
CompareFilter.CompareOp.EQUAL等
PrefixFilter 需要的参数 prefix
PageFilter 需要的参数 pageSize
ColumnPrefixFilter 需要的参数 prefix
这样设计的话 需要 将RowFilter ,PrefixFilter ,PageFilter 进行封装
感觉上 还是没有屏蔽hbase
设计方案二 已语义的方法 去做这件事情,但这种方法接口会非常多
getListByPrefixRowkeyAndRegexRowKey
getListByPrefixRowkeyAndPageFilter
为什么 我们不能提供一个 更简单的方法呢
类似于 hue hbase的脚本 或者 直接 让业务方 传sql 呢 比如说 phoenix
1 . 同样能解决 hbase put ,scan 操作 ,开发比较吃力 ,不优雅的问题
2 . sql 谁都会
但是 phoenix 性能是否能搞定呢 。
基于他的例子 表结构如下图
CREATE TABLE IF NOT EXISTS %s (HOST CHAR(2) NOT NULL,
DOMAIN VARCHAR NOT NULL,
FEATURE VARCHAR NOT NULL,
DATE DATE NOT NULL,
USAGE.CORE BIGINT,
USAGE.DB BIGINT,
STATS.ACTIVE_VISITOR INTEGER
CONSTRAINT PK PRIMARY KEY (HOST, DOMAIN, FEATURE, DATE))
SPLIT ON ('CSGoogle','CSSalesforce','EUApple','EUGoogle','EUSalesforce','NAApple','NAGoogle','NASalesforce')
而 实际 hbase 表结构 如下图
hbase(main):005:0> scan 'PERFORMANCE_20000000' , { FILTER=>" RowFilter(=,'regexstring:Apple.com')" ,LIMIT=>5}
ROW COLUMN+CELL
CSApple.com\x00Dashboard\x00\x80\x00\x01lQ\xDC\xCEh column=STATS:\x80\x0D, timestamp=1564741554988, value=\x80\x00&S
CSApple.com\x00Dashboard\x00\x80\x00\x01lQ\xDC\xCEh column=USAGE:\x00\x00\x00\x00, timestamp=1564741554988, value=x
CSApple.com\x00Dashboard\x00\x80\x00\x01lQ\xDC\xCEh column=USAGE:\x80\x0B, timestamp=1564741554988, value=\x80\x00\x00\x00\x00\x00\x01\x09
CSApple.com\x00Dashboard\x00\x80\x00\x01lQ\xDC\xCEh column=USAGE:\x80\x0C, timestamp=1564741554988, value=\x80\x00\x00\x00\x00\x00\x05\xCA
CSApple.com\x00Dashboard\x00\x80\x00\x01lQ\xDE\x1EX column=STATS:\x80\x0D, timestamp=1564741555106, value=\x80\x00\x1EM
CSApple.com\x00Dashboard\x00\x80\x00\x01lQ\xDE\x1EX column=USAGE:\x00\x00\x00\x00, timestamp=1564741555106, value=x
CSApple.com\x00Dashboard\x00\x80\x00\x01lQ\xDE\x1EX column=USAGE:\x80\x0B, timestamp=1564741555106, value=\x80\x00\x00\x00\x00\x00\x00\xA0
CSApple.com\x00Dashboard\x00\x80\x00\x01lQ\xDE\x1EX column=USAGE:\x80\x0C, timestamp=1564741555106, value=\x80\x00\x00\x00\x00\x00\x07A
CSApple.com\x00Dashboard\x00\x80\x00\x01lQ\xDEp` column=STATS:\x80\x0D, timestamp=1564741555106, value=\x80\x00\x01\x1F
CSApple.com\x00Dashboard\x00\x80\x00\x01lQ\xDEp` column=USAGE:\x00\x00\x00\x00, timestamp=1564741555106, value=x
CSApple.com\x00Dashboard\x00\x80\x00\x01lQ\xDEp` column=USAGE:\x80\x0B, timestamp=1564741555106, value=\x80\x00\x00\x00\x00\x00\x00)
CSApple.com\x00Dashboard\x00\x80\x00\x01lQ\xDEp` column=USAGE:\x80\x0C, timestamp=1564741555106, value=\x80\x00\x00\x00\x00\x00\x03f
CSApple.com\x00Dashboard\x00\x80\x00\x01lQ\xDE\xED` column=STATS:\x80\x0D, timestamp=1564741555106, value=\x80\x00\x0DW
CSApple.com\x00Dashboard\x00\x80\x00\x01lQ\xDE\xED` column=USAGE:\x00\x00\x00\x00, timestamp=1564741555106, value=x
CSApple.com\x00Dashboard\x00\x80\x00\x01lQ\xDE\xED` column=USAGE:\x80\x0B, timestamp=1564741555106, value=\x80\x00\x00\x00\x00\x00\x019
CSApple.com\x00Dashboard\x00\x80\x00\x01lQ\xDE\xED` column=USAGE:\x80\x0C, timestamp=1564741555106, value=\x80\x00\x00\x00\x00\x00\x02(
CSApple.com\x00Dashboard\x00\x80\x00\x01lQ\xDF\xB4\x98 column=STATS:\x80\x0D, timestamp=1564741555156, value=\x80\x00\x0EQ
CSApple.com\x00Dashboard\x00\x80\x00\x01lQ\xDF\xB4\x98 column=USAGE:\x00\x00\x00\x00, timestamp=1564741555156, value=x
CSApple.com\x00Dashboard\x00\x80\x00\x01lQ\xDF\xB4\x98 column=USAGE:\x80\x0B, timestamp=1564741555156, value=\x80\x00\x00\x00\x00\x00\x01\x06
CSApple.com\x00Dashboard\x00\x80\x00\x01lQ\xDF\xB4\x98 column=USAGE:\x80\x0C, timestamp=1564741555156, value=\x80\x00\x00\x00\x00\x00\x01[
HOST, DOMAIN, FEATURE, DATE 构成了 rowkey
我发现了 个问题 2000 w 的数据量
select * from PERFORMANCE_20000000 where DOMAIN = 'Apple.com' limit 5
+-------+------------+------------+--------------------------+-------+-------+-----------------+
| HOST | DOMAIN | FEATURE | DATE | CORE | DB | ACTIVE_VISITOR |
+-------+------------+------------+--------------------------+-------+-------+-----------------+
| CS | Apple.com | Dashboard | 2019-08-02 10:25:21.000 | 265 | 1482 | 9811 |
| CS | Apple.com | Report | 2019-10-14 07:19:58.000 | 302 | 1628 | 5185 |
| EU | Apple.com | Dashboard | 2019-08-02 10:25:26.000 | 346 | 981 | 673 |
| EU | Apple.com | Report | 2019-10-14 02:35:55.000 | 268 | 325 | 989 |
| NA | Apple.com | Dashboard | 2019-08-02 10:24:59.000 | 343 | 907 | 4117 |
+-------+------------+------------+--------------------------+-------+-------+-----------------+
5 rows selected (10.726 seconds)
需要10 s 时间
而基于 hbase shell 只需要 36ms
scan 'PERFORMANCE_20000000' , { FILTER=>" RowFilter(=,'regexstring:Apple')" ,LIMIT=>5} 而且 返回数据不同 。
也就是说 phoenix 不是 按照 hbase shell 这种方式去执行的 。 为什么呢
是不是SPLIT ON ('CSGoogle','CSSalesforce','EUApple','EUGoogle','EUSalesforce','NAApple','NAGoogle','NASalesforce') 手动设计分区的原因呢
好 我们可以在创建一张测试表 PERFORMANCE2_20000000 ,数据量 只有200w ,没有手动分区 。 测试结果 性能是可以的
select * from PERFORMANCE_2500000 where DOMAIN = 'Apple.com' limit 5 ;
+-------+------------+------------+--------------------------+-------+-------+-----------------+
| HOST | DOMAIN | FEATURE | DATE | CORE | DB | ACTIVE_VISITOR |
+-------+------------+------------+--------------------------+-------+-------+-----------------+
| CS | Apple.com | Dashboard | 2019-08-07 07:15:20.000 | 467 | 1178 | 612 |
| NA | Apple.com | Report | 2019-08-09 03:01:27.000 | 418 | 1196 | 6805 |
| CS | Apple.com | Dashboard | 2019-08-07 07:15:33.000 | 465 | 1216 | 1192 |
| NA | Apple.com | Report | 2019-08-09 03:01:58.000 | 34 | 608 | 9394 |
| CS | Apple.com | Dashboard | 2019-08-07 07:16:39.000 | 219 | 589 | 8231 |
+-------+------------+------------+--------------------------+-------+-------+-----------------+
5 rows selected (0.029 seconds)
创建一张新的表 和 hbase 结构完全一样 ,数据量250w 。ok 完全可以
image.png
以上问题 理论上 应该是 phoenix 在多主键 且查询 无第一主键的情况 选择错误的执行计划 ,而如果 按hbase 表单结构设计则无此问题 。
那我们换一种想法 , phoenix 用了数据库思想做了 些他不该做的事情 那么 ,我们可不可以 去掉它的执行计划 ,按hbase的方式来呢 。官网 有这个例子 。
Use the SKIP_SCAN hint to force a skip scan to be performed on the query when it otherwise would not be. This option may improve performance if a query does not include the leading primary key column, but does include other, very selective primary key columns.
0: jdbc:phoenix:localhost:2181:/hbase> select /*+ SKIP_SCAN */ * from PERFORMANCE_20000000 where DOMAIN = 'Apple.com' limit 5
. . . . . . . . . . . . . . . . . . .> ;
+-------+------------+------------+--------------------------+-------+-------+-----------------+
| HOST | DOMAIN | FEATURE | DATE | CORE | DB | ACTIVE_VISITOR |
+-------+------------+------------+--------------------------+-------+-------+-----------------+
| CS | Apple.com | Dashboard | 2019-08-06 02:45:34.000 | 136 | 95 | 174 |
| EU | Apple.com | Dashboard | 2019-08-06 02:46:06.000 | 432 | 1691 | 4943 |
| NA | Apple.com | Dashboard | 2019-08-06 02:47:03.000 | 408 | 1977 | 9440 |
| CS | Apple.com | Dashboard | 2019-08-06 02:46:57.000 | 71 | 494 | 2306 |
| EU | Apple.com | Dashboard | 2019-08-06 02:46:18.000 | 202 | 1097 | 6735 |
+-------+------------+------------+--------------------------+-------+-------+-----------------+
5 rows selected (0.068 seconds)
0: jdbc:phoenix:localhost:2181:/hbase> select /*+ SKIP_SCAN +NO_INDEX */ * from PERFORMANCE_20000000 where DOMAIN = 'Apple.com' limit 5 ;
+-------+------------+------------+--------------------------+-------+-------+-----------------+
| HOST | DOMAIN | FEATURE | DATE | CORE | DB | ACTIVE_VISITOR |
+-------+------------+------------+--------------------------+-------+-------+-----------------+
| CS | Apple.com | Dashboard | 2019-08-06 02:45:34.000 | 136 | 95 | 174 |
| EU | Apple.com | Dashboard | 2019-08-06 02:46:06.000 | 432 | 1691 | 4943 |
| NA | Apple.com | Dashboard | 2019-08-06 02:47:03.000 | 408 | 1977 | 9440 |
| CS | Apple.com | Dashboard | 2019-08-06 02:46:57.000 | 71 | 494 | 2306 |
| EU | Apple.com | Dashboard | 2019-08-06 02:46:18.000 | 202 | 1097 | 6735 |
+-------+------------+------------+--------------------------+-------+-------+-----------------+
阿里同学文章写得很详细
https://yq.aliyun.com/articles/703818?spm=a2c4e.11155435.0.0.c1434db44lHjaV
还有篇文章 写得也是极好
https://blog.csdn.net/rlnLo2pNEfx9c/article/details/78692969
然后
mop-hbase-template 不是基于 spring boot hbase template 来的
而且使用的hbase-client版本为0.96