Flink实时数仓项目—DWS层设计与实现

本文详细介绍了使用Flink构建实时数仓的DWS层设计与实现,涵盖访客主题和商品主题宽表的构建,包括需求梳理、表设计、实现思路及代码实现,旨在实现轻度聚合和主题数据管理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


前言

在前面通过使用分流等方法,把数据拆分成了独立的Kafka Topic,接下来我们就要根据需求得出要计算哪些指标项。我们把指标以主题宽表的形式输出就是DWS层要做的事情。


一、需求梳理

1.需求梳理

项目需求整理如下:
在这里插入图片描述

2.DWS层定位

1)轻度聚合。因为DWS层要应对很多事实查询,如果是完全的明细那么查询的压力是非常大的。
2)将更多的实时数据以主题的方式组合起来便于管理,同时也能减少维度查询的次数。

二、DWS层—访客主题宽表的实现

1.访客主题的需求

在这里插入图片描述

2.访客主题宽表的设计

DWS层中一张主题宽表包括两部分内容:维度数据+度量值(事实数据)
维度数据:维度数据就是数据里面的一些维度信息,这里就指的是日志数据里面的渠道、地区、版本、新老用户等这些数据
度量值:度量值就是上面的需求指标,这里就是PV、UV、跳出次数(这里不是要算出具体结果的,只是轻度聚合)、进入页面次数、连续访问时长

3.实现思路

在观察统计出来的需求表,一些需求指标需要的是DWD层的数据,一些需求指标需要的是DWM层的数据,所以对应了好几个Topic主题,我们要从不同的主题中读取数据,然后把读取到的数据转化为定义的主题宽表的对象,然后再UNION起来。

4.代码实现

1)首先,我们要建一个访客主题的宽表的实体类,用来保存结果。我们需要渠道、地区、新老用户标识、版本这四个维度(根据需求来,其余维度可选),其次,我们的度量值是独立访客数、页面访问数、首次进入页面次数、跳出页面次数、访问页面时间这几个度量值,然后因为是开窗统计,所以为了区分是哪个窗口,要加上窗口开始时间和结束时间。最后,开窗需要事件时间语义,那么就需要提取时间戳,所以要一个时间戳字段,实体类如下:

@Data
@AllArgsConstructor
public class VisitorStats {
   
    //统计开始时间,为了区分是哪个窗口
    private String stt;
    //统计结束时间
    private String edt;
    //维度:版本
    private String vc;
    //维度:渠道
    private String ch;
    //维度:地区
    private String ar;
    //维度:新老用户标识
    private String is_new;
    //度量:独立访客数
    private Long uv_ct=0L;
    //度量:页面访问数
    private Long pv_ct=0L;
    //度量: 进入次数
    private Long sv_ct=0L;
    //度量: 跳出次数
    private Long uj_ct=0L;
    //度量: 持续访问时间
    private Long dur_sum=0L;
    //统计时间
    private Long ts;
}

2)我们根据需求可以知道,总共需要Kafka的dwd_page_log、dwm_unique_visit、dwm_user_jump_detail三个主题的数据,所以,先从这些主题中读取相应的数据,代码如下:

    //2、分别从对应的主题中读取需要的数据
    String pageViewSourceTopic = "dwd_page_log";
    String uniqueVisitSourceTopic = "dwm_unique_visit";
    String userJumpDetailSourceTopic = "dwm_user_jump_detail";
    String groupId = "visitor_stats_app";

    DataStreamSource<String> pageViewDS = env.addSource(MyKafkaUtil.getKafkaSource(pageViewSourceTopic, groupId));
    DataStreamSource<String> uniqueVisitDS = env.addSource(MyKafkaUtil.getKafkaSource(uniqueVisitSourceTopic, groupId));
    DataStreamSource<String> userJumpDS = env.addSource(MyKafkaUtil.getKafkaSource(userJumpDetailSourceTopic, groupId));

3)读取到相应的数据后,拿dwm_user_jump_detail这个主题来说,这个主题存放的是用户跳出页面的次数,它只是用来求这部分的数据的,所以它求的是VisitorStats实体类中的uj_ct这个变量值;dwm_unique_visit这个主题里是只求uv_ct这个变量值的;dwd_page_log这个主题是最全的页面日志,用来求页面总访问次数、页面浏览时间和第一次进入页面的次数的。
**可以看出,这三个流分别求的是实体类中不同的字段,所以我们可以先将它们转化为实体类,将自己能够求的值设置为1或别的,然后将三个流union起来,再进行聚合操作,就可以得到在一个窗口内的一个实体类,这个实体类里面的变量是最终的和。**代码如下:

    //3、将数据的格式转换成相同的主题宽表对象格式
    //处理PV数据
    SingleOutputStreamOperator<VisitorStats> visitorStatsWithPvDS = pageViewDS.map(data -> {
   
        //页面日志数据可以统计出PV、进入页面数、连续访问时长
        JSONObject jsonObject = JSON.parseObject(data);
        //获取维度信息
        JSONObject common = jsonObject.getJSONObject("common");
        //获取页面信息
        JSONObject page = jsonObject.getJSONObject("page");
        //获取上个页面id
        String last_page_id = page.getString("last_page_id");
        //判断上个页面id是否为null
        Long sv=0L;
        if(last_page_id==null || last_page_id.length()==0)
            sv=1L;
        return new VisitorStats("", "",
                common.getString("vc"),
                common.getString("ch"),
                common.getString("ar"),
                common.getString("is_new"),
                0L, 1L, sv, 0L, page.getLong("during_time"),
                jsonObject.getLong("ts"));
    });

    //处理UV数据
    SingleOutputStreamOperator<VisitorStats> visitorStatsWithUvDS = uniqueVisitDS.map(data -> {
   
        //UV数据可以统计出uv数据
        JSONObject jsonObject = JSON.parseObject(data);
        //获取维度信息
        JSONObject common = jsonObject.getJSONObject("common");
        return new VisitorStats("", "",
                common.getString("vc"),
                common.getString("ch"),
                common.getString("ar"),
                common.getString("is_new"),
                1L, 0L, 0L, 0L, 0L,
                jsonObject.getLong("ts"));
    });

    //处理UJ数据
    SingleOutputStreamOperator<VisitorStats> visitorStatsWithUjDS = userJumpDS.map(data -> {
   
        //UJ数据可以统计出跳出页面的次数
        JSONObject jsonObject = JSON.parseObject(data);
        //获取维度信息
        JSONObject common = jsonObject.getJSONObject("common");
        return new VisitorStats("", "",
                common.getString("vc"),
                common.getString("ch"),
                common.getString("ar"),
                common.getString("is_new"),
                0L, 0L, 0L, 1L, 0L,
                jsonObject.getLong("ts"));
    });


    //4、将几个流Union起来
    DataStream<VisitorStats> unionDS = visitorStatsWithPvDS.union(visitorStatsWithUvDS, visitorStatsWithUjDS);

4)经过上面的步骤得到了union之后的流,我们要开窗做计算,所以需要提取水位线生成时间戳,然后开窗计算。因为这里需要提取窗口的开始时间和结束时间,所以可以选择process或者增量函数+全窗口函数。这里选择增量函数+全窗口函数的方法在增量函数中进行规约计算,然后将结果发送到全窗口函数中进行加工,提取窗口开始时间和结束时间,再向下游进行传输。代码如下:

    //6、按照维度信息进行分组
    KeyedStream<VisitorStats, Tuple4<String, String, String, String>> keyedStream = visitorStatsWithWMDS.keyBy(data -> Tuple4.of(data.getVc(), data.getCh(), data.getAr(), data.getIs_new()));

    //7、开窗聚合,10s的滚动窗口
    SingleOutputStreamOperator<VisitorStats> resultDS = keyedStream.window(TumblingEventTimeWindows.of(Time.seconds(10)))
            .reduce(new ReduceFunction<VisitorStats>() {
   
                @Override
                public VisitorStats reduce(VisitorStats visitorStats, VisitorStats t1) throws Exception {
   
                    return new VisitorStats("", "",
                            visitorStats.getVc(),
                            visitorStats.getCh(),
                            visitorStats.getAr(),
                            visitorStats.getIs_new(),
                            visitorStats.getUv_ct() + t1.getUv_ct
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值