hbase环境准备
数据准备
Hbase作为维度表进行temporal table join的场景
Flink SQL做计算写Hbase的场景
总结
01 Hbase 环境准备
由于没有测试的Hbase环境以及为了避免污染线上Hbase环境。因此,自己build一个 Hbase docker image(大家可以docker pull guxinglei/myhbase 拉到本地),是基于官方干净的ubuntu imgae之上安装了Hbase 2.2.0版本以及JDK1.8版本。
启动容器,暴露Hbase web UI 端口以及内置zk端口,方便我们从web 页面看信息以及创建Flink Hbase table需要zk的链接信息
docker run -it --network=host -p 2181:2181 -p 60011:60011 docker.io/guxinglei/myhbase:latest bash
进入容器,启动hbase集群,以及启动rest server,后续方便我们用REST API来读取Flink SQL写进Hbase的数据
# 启动hbase 集群bin/start-hbase.sh# 后台启动restServerbin/hbase-daemon.sh start rest -p 8000
02 数据准备
由于hbase环境是自己临时搞的单机服务,里面没有数据,需要往里面写点数据供后续示例用。在Flink SQL实战系列第二篇中介绍了如何注册Flink Mysql table,我们可以将广告位表抽取到hbase表中,用来做维度表,进行temporal table join。因此,我们需要在Hbase中创建一张表,同时还需要创建Flink Hbase table, 这两张表通过Flink SQL 的Hbase connector关联起来。
在容器中启动hbase shell,创建一张名为dim_hbase的hbase 表,建表语句如下所示:
# 在hbase shell创建 hbase表hbase(main):002:0> create 'dim_hbase','cf'Created table dim_hbaseTook 1.3120 seconds=> Hbase::Table - dim_hbase
在Flink 中创建Flink Hbase table,建表语句如下所示:
# 注册 Flink Hbase tableDROP TABLE IF EXISTS flink_rtdw.demo.hbase_dim_table;CREATE TABLE flink_rtdw.demo.hbase_dim_table ( rowkey STRING, cf ROW < adspace_name STRING >, PRIMARY KEY (rowkey) NOT ENFORCED) WITH ( 'connector' = 'hbase-1.4', 'table-name' = 'dim_hbase', 'sink.buffer-flush.max-rows' = '1000', 'zookeeper.quorum' = 'localhost:2181');
Flink MySQL table 和 Flink Hbase table已经创建好了,就可以写抽取数据到Hbase的SQL job了,SQL 语句以及job状态如下所示:
# 抽取Mysql数据到Hbase表中insert into hbase_dim_tableselect CAST (ID as VARCHAR), ROW(name)from mysql_dim_table;
03 HBase 作为维表与Kafka 做temporal join的场景
在Flink SQL join中,维度表的join一定绕不开的,比如订单金额join 汇率表,点击流join广告位的明细表等等,使用场景非常广泛。那么作为分布式数据库的Hbase比MySQL作为维度表用作维度表join更有优势。在Flink SQL实战系列第二篇中,我们注册了广告的点击流,将kafka topic注册Flink Kafka Table,同时也介绍了temporal table join在Flink SQL中的使用;那么本节中将会介绍Hbase作为维度表来使用,上面小节中已经将数据抽取到Hbase中了,我们直接写temporal table join 计算逻辑即可。
作为广告点击流的Flink Kafa table 与 作为广告位的Flink Hbase table通过广告位Id进行temporal table join,输出广告位Id和广告位中文名字,SQL join 逻辑如下所示:
select adsdw_dwd_max_click_mobileapp.publisher_adspace_adspaceId as publisher_adspace_adspaceId, hbase_dim_table.cf.adspace_name as publisher_adspace_namefrom adsdw_dwd_max_click_mobileappleft join hbase_dim_table FOR SYSTEM_TIME AS OF adsdw_dwd_max_click_mobileapp.procTimeon cast(adsdw_dwd_max_click_mobileapp.publisher_adspace_adspaceId as string) = hbase_dim_table.rowkey;
temporal table join job提交Flink集群上的状态以及join结果如下所示:
04 计算结果sink 到Hbase作为结果的场景
上面小节中,Hbase作为维度表用作temporal table join是非常常见的场景,实际上Hbase作为存储计算结果也是非常常见的场景,毕竟Hbase作为分布式数据库,底层存储是拥有多副本机制的HDFS,维护简单,扩容方便, 实时查询快,而且提供各种客户端方便下游使用存储在Hbase中的数据。那么本小节就介绍Flink SQL将计算结果写到Hbase,并且通过REST API查询计算结果的场景。
进入容器中,在hbase 中新建一张hbase 表,一个column family就满足需求,建表语句如下所示:
# 注册hbase sink tablecreate 'dwa_hbase_click_report','cf'
建立好hbase 表之后,我们需要在Flink SQL创建一张Flink Hbase table,这个时候我们需要明确cf 这个column famaly下面column字段,在Flink SQL实战第二篇中,已经注册好了作为点击流的Flink Kafka table,因此本节中,将会计算点击流的uv和点击数,因此两个column分别为uv和click_count,建表语句如下所示:
# 注册 Flink Hbase tableDROP TABLE IF EXISTS flink_rtdw.demo.dwa_hbase_click_report;CREATE TABLE flink_rtdw.demo.dwa_hbase_click_report ( rowkey STRING, cf ROW < uv BIGINT, click_count BIGINT >, PRIMARY KEY (rowkey) NOT ENFORCED) WITH ( 'connector' = 'hbase-1.4', 'table-name' = 'dwa_hbase_click_report', 'sink.buffer-flush.max-rows' = '1000', 'zookeeper.quorum' = 'hostname:2181');
前面点击流的Flink Kafka table 和存储计算结果的Hbase table 和Flink Hbase table已经准备了,我们将做一个1分钟的翻转窗口计算uv和点击数,并且将计算结果写到Hbase中。对Hbase了解的人应该知道,rowkey的设计对 hbase regoin的分布有着非常重要的影响,基于此我们的rowkey是使用Flink SQL内置的reverse函数进行广告位Id进行反转和窗口启始时间做concat,因此,SQL逻辑语句如下所示:
INSERT INTO dwa_hbase_click_reportSELECT CONCAT(REVERSE(CAST(publisher_adspace_adspaceId AS STRING)) , '_', CAST((UNIX_TIMESTAMP(DATE_FORMAT(TUMBLE_START(ets, INTERVAL '1' MINUTE),'yyyy-MM-dd HH:mm:ss')) * 1000) AS STRING) ) as rowkey, ROW(COUNT(DISTINCT audience_mvid) , COUNT(audience_behavior_click_creative_impressionId)) as cfFROM adsdw_dwd_max_click_mobileappWHERE publisher_adspace_adspaceId IS NOT NULL AND audience_mvid IS NOT NULL AND audience_behavior_click_creative_impressionId IS NOT NULLGROUP BY TUMBLE(ets, INTERVAL '1' MINUTE), publisher_adspace_adspaceId;
SQL job提交之后的状态以及结果check如下所示:
上述SQL job已经成功的将结算结果写到Hbase中了。对于线上的hbase 服务来讲,很多同事不一定有hbase 客户端的权限,从而也不能通过hbase shell 读取数据;另外作为线上报表服务显然不可能通过hbase shell来通过查询数据。因此,在实时报表场景中,数据开发工程师将数据写入Hbase, 前端工程师 通过REST API来读取数据。前面我们已经启动了hbase rest server进程,我们可以通rest服务提供读取hbase里面的数据。
我们先get一条刚刚写到hbase中的数据看看,如下所示:
下面我们开始通过REST API 来查询hbase 中的数据,第一步,执行如下语句拿到scannerId;首先需要将要查询的rowkey进行base64编码才能使用,后面需要将结果进行base64解码
rowkey base64编码前:0122612_1606295280000
base64编码之后:MDEyMjYxMl8xNjA2Mjk1MjgwMDAw
curl -vi -X PUT \ -H "Accept: text/xml" \ -H "Content-Type: text/xml" \ -d '"MDEyMjYxMl8xNjA2Mjk1MjgwMDAw" endRow= "http://hostname:8000/dwa_hbase_click_report/scanner"
第二步,执行如下语句根据上条语句返回的scannerID查询数据,可以看到返回的结果:
curl -vi -X GET \ -H "Accept: application/json" \ "http://hostname:8000/dwa_hbase_click_report/scanner/16063768141736ac0a8b5"
第三步,查询完毕之后,执行如下语句删除该scannerId:
curl -vi -X DELETE \ -H "Accept: text/xml" \ "http://hostname:8000/dwa_hbase_click_report/scanner/16063768141736ac0a8b5"
五. 总结
在本篇文章中,我们介绍了Hbase 和Flink SQL的结合使用比较广泛两种的场景: 作为维度表用以及存储计算结果; 同时使用REST API对hbase中的数据进行查询,对于查询用户来说,避免直接暴露hbase的zk,同时将rest server和hbase集群解耦。
作者简介
余敖,360数据开发高级工程师,目前专注于基于Flink的实时数仓建设与平台化工作。对Flink、Kafka、Hive、Spark等进行数据ETL和数仓开发有丰富的经验。