Flink实时数仓项目—DWD层设计与实现
前言
在前面已经将日志数据进行了分流写入到了Kafka的不同主题,也已经将业务数据中的事实数据放到了Kafka,维度数据放到了HBase中,接下来要进行DWM层的设计。
DWM 层的定位是什么,DWM 层主要服务 DWS,因为部分需求直接从 DWD 层到DWS 层中间会有一定的计算量,而且这部分计算的结果很有可能被多个 DWS 层主题复用,所以部分 DWD 成会形成一层 DWM,我们这里主要涉及业务。
一、DWM层设计
1.设计思路
DWM层是位于DWD到DWS之间的一层,实际上是作为复用进行分层的。所以就需要先列出DWS层的所有统计指标,看看哪些是重复计算的(或者是需要加工的,形成宽表的),这些就需要在DWM中进行设计和实现。
(在离线数仓中有DWT层,实时数仓中没有的原因:因为DWT是做的历史的聚集,是累计结果,实时数仓中不需要累积数据。)
2.DWS层需求梳理
我们可以看到,红色字体部分的要么是需要加工处理形成宽表,要么是进行了重复的处理,所以这些部分可以在DWM层做好,提高复用性。
2.DWM层需求整理
从上面可以看出,DWM层主要有以下几个需求:
1)访问的UV计算
2)跳出的明细计算
3)订单宽表
4)支付款表
二、功能一:访客UV计算的设计与实现
1.需求说明
UV,全称是Unique Vistor,即独立访客,对于实时计算中,也可以称为DAU(Daily Active User),即每日活跃用户,实时计算中UV指的是当日的访客数量。
2.需求分析
要想从用户行为日志中识别出当日的访客,有以下几个步骤:
1)要明确什么算是当日的访客
一个页面信息中包含了上一个页面id这个字段,如果一个人在前一天晚上登录了app,一直看到了第二天早上,那么这不算当天登录。
当天登录要求观看的页面是第一个页面,且日期为今天。意思就是页面日志信息里的上一个页面必须为空,且日期在今天。
2)对用户进行去重
因为一个用户可能在一天内多次登录,即有多个上个页面为空的页面信息,所以我们需要对用户进行去重。
3.代码实现
我们从Kafka的dwd_page_log主题中拿到对应的页面日志数据,按照mid进行分组,分别保留每一个用户第一次登录的数据。同时定义一个状态,保存用户的登陆日期,这样如果每来一条数据,如果该数据的登录日期和当前状态的数据一样,就舍弃;反之,就代表为当天的第一次登录的数据,保留下来。
需要注意的是:可能一个用户很长时间才登录一次,那么状态就保留了很长时间,这是没有必要的,因此需要设置状态的保留时间TTL为24小时即可。
代码如下:
//2、读取Kafka的 dwd_page_log 主题的数据
//消费者组
String groupId="unique_visit_app";
//消费的主题
String sourceTopic="dwd_page_log";
//最终要写入的主题
String sinkTopic="dwm_unique_visit";
DataStreamSource<String> kafkaDS = env.addSource(MyKafkaUtil.getKafkaSource(sourceTopic, groupId));
//3、将每行数据转化为JSON对象
SingleOutputStreamOperator<JSONObject> jsonObjDS = kafkaDS.map(data -> JSON.parseObject(data));
//4、过滤出来上个页面为空的数据 状态编程 只保留每个mid每天第一次登录的数据
//获取common里的mid字段,按mid分组
KeyedStream<JSONObject, String> keyedStream = jsonObjDS.keyBy(jsonObject -> jsonObject.getJSONObject("common").getString("mid"));
SingleOutputStreamOperator<JSONObject> UvDS = keyedStream.filter(new RichFilterFunction<JSONObject>() {
private ValueState<String> valueState;
private SimpleDateFormat simpleDateFormat;
@Override
public void open(Configuration parameters) throws Exception {
ValueStateDescriptor