Flink实例(五十): Operators(十)多流转换算子(五)coGroup 与union
参考链接:https://mp.weixin.qq.com/s/BOCFavYgvNPSXSRpBMQzBw
需求场景分析
需求场景
需求诱诱诱来了。。。数据产品妹妹想要统计单个短视频粒度的「点赞,播放,评论,分享,举报」五类实时指标,并且汇总成 photo_id、1 分钟时间粒度的实时视频消费宽表(即宽表字段至少为:「photo_id + play_cnt + like_cnt + comment_cnt + share_cnt + negative_cnt + minute_timestamp」)产出至实时大屏。
问题在于对同一个视频,五类视频消费行为的触发机制以及上报时间是不同,也就决定了对实时处理来说五类行为日志对应着五个不同的数据源。sql boy 们自然就想到了 join 操作将五类消费行为日志合并,可是实时 join(cogroup) 真的那么完美咩~,下文细谈。
source 输入以及特点
首先分析下需求中的 source 特点:
- photo_id 粒度 play(播放)、like(点赞)、comment(评论)、share(分享)、negative(举报)明细数据,「用户播放(点赞、评论...)n 次,客户端\服务端就会上传 n 条播放(点赞、评论...)日志至数据源」
- 五类视频消费行为日志的 source schema 都为:「photo_id + timestamp + 其他维度」
sink 输出以及特点
sink 特点如下:
- photo_id 粒度 play(播放)、like(点赞)、comment(评论)、share(分享)、negative(举报)「1 分钟级别窗口聚合数据」
- 实时视频消费宽表 sink schema 为:「photo_id + play_cnt + like_cnt + comment_cnt + share_cnt + negative_cnt + minute_timestamp」
source、sink 样例数据
source 数据:
| photo_id | timestamp | user_id | 说明 |
|---|---|---|---|
| 1 | 2020/10/3 11:30:33 | 3 | 播放 |
| 1 | 2020/10/3 11:30:33 | 4 | 播放 |
| 1 | 2020/10/3 11:30:33 | 5 | 播放 |
| 1 | 2020/10/3 11:30:33 | 4 | 点赞 |
| 2 | 2020/10/3 11:30:33 | 5 | 点赞 |
| 1 | 2020/10/3 11:30:33 | 5 | 评论 |
sink 数据:
| photo_id | timestamp | play_cnt | like_cnt | comment_cnt |
|---|---|---|---|---|
| 1 | 2020/10/3 11:30:00 | 3 | 1 | 1 |
| 2 | 2020/10/3 11:30:00 | 0 | 1 | 0 |
我们已经对数据源输入和输出有了完整的分析,那就瞧瞧有什么方案可以实现上述需求吧。
实现方案
- 方案1:「本小节 cogroup 方案」直接消费原始日志数据,对五类不同的视频消费行为日志使用 cogroup 或者 join 进行窗口聚合计算
- 方案2:对五类不同的视频消费行为日志分别单独聚合计算出分钟粒度指标数据,下游再对聚合好的指标数据按照 photo_id 进行合并
- 方案3:「本小节 union 方案」既然数据源 schema 相同,直接对五类不同的视频消费行为日志做 union 操作,在后续的窗口函数中对五类指标进行聚合计算。后文介绍 union 方案的设计过程
先上 cogroup 方案的示例代码。
cogroup
cogroup 实现示例如下,示例代码直接使用了处理时间(也可替换为事件时间~),因此对数据源的时间戳做了简化(直接干掉):
public class Cogroup {
public static void main(String[] args) throws Exception {
final StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
// Long -> photo_id 播放一次
DataStream<Long> play = SourceFactory.getDataStream(xxx);
// Long -> photo_id 点赞一次
DataStream<Long> like = SourceFactory.getDataStream(xxx);
// Long -> photo_id 评论一次
DataStream<Long> comment = SourceFactory.getDataStream(xxx);
// Long -> photo_id 分享一次
DataStream<Long> share = SourceFactory.getDataStream(xxx);
// Long -> photo_id 举报一次
DataStream<Long> negative = SourceFactory.getDataStream(xxx);
// Tuple3<Long, Long, Long> -> photo_id + play_cnt + like_cnt 播放和点赞的数据合并
DataStream<Tuple3<Long, Long, Long>> playAndLikeCnt = play
.coGroup(like)
.where(KeySelectorFactory.get(Function.identity()))
.equalTo(KeySelectorFactory.get(Function.identity()))
.window(TumblingProcessingTimeWindows.of(Time.seconds(60)))
.apply(xxx1);
// Tuple4<Long, Long, Long, Long> -> photo_id + play_cnt + like_cnt + comment_cnt 播放、点赞、评论的数据合并
DataStream<Tuple4<Long, Long, Long, Long, Long>> playAndLikeAndComment = playAndLikeCnt
.coGroup(comment)
.where(KeySelectorFactory.get(playAndLikeModel -> playAndLikeModel.f0))
.equalTo(KeySelectorFactory.get(Function.identity()))
.window(TumblingProcessingTimeWindows.of(Time.seconds(60)))
.apply(xxx2);
// Tuple5<Long, Long, Long, Long, Long> -> photo_id + play_cnt + like_cnt + comment_cnt + share_cnt 播放、点赞、评论、分享的数据合并
DataStream<Tuple5<Long, Long, Long, Long, Long, Long>> playAndLikeAndCommentAndShare = playAndLikeAndComment
.coGroup(share)
.where(KeySelectorFactory.get(playAndLikeAndCommentModel -> playAndLikeAndCommentModel.f0))
.equalTo(KeySelectorFactory.get(Function.identity()))
.window(TumblingProcessingTimeWindows.of(Time.seconds(60)))
.apply(xxx2);
// Tuple7<Long, Long, Long, Long, Long, Long, Long> -> photo_id + play_cnt + like_cnt + comment_cnt + share_cnt + negative_cnt + minute_timestamp 播放、点赞、评论、分享、举报的数据合并
// 同上~
DataStream<Tuple7<Long, Long, Long, Long, Long, Long, Long>> playAndLikeAndCommentAndShare = ***;
env.execute();
}
}
粗暴一想,上面这样一搞不就结束了么,事情没那么简单,我们来做一个详细点的分析。
上述实现可能会存在的问题点
- 「从 flink 消费到 play 数据源的一条数据到最终产出这条数据被聚合后的数据,整个过程的数据延迟 > 3 分钟...」
- 「如果数据源持续增加(比如添加其他视频消费操作数据源),则整个任务算子变多,数据链路更长,任务稳定性会变差,产出数据延迟也会随着窗口计算变多,延迟更久」
逆推链路
1 - 5 为逆推的整条链路。
- 「1.五类指标的数据都在单个窗口中计算」
- 「2.五类指标的窗口 model 相同」
- 「3.keyby 中的 key 一致(photo_id)」
- 「4.五类指标的数据源都为 photo_id 粒度,并且五类数据源的 model 都必须相同,并且可以做合并」
- 「5.union 算子可以对五类数据源做合并!!!」
话不多说直接上 union 方案代码。
public class Union {
public static void main(String[] args) throws Exception {
final StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
// Tuple2<Long, String> -> photo_id + "PLAY"标签
DataStream<Tuple2<Long, String>> play = SourceFactory.getDataStream(xxx);
// Tuple2<Long, String> -> photo_id + "LIKE"标签
DataStream<Tuple2<Long, String>> like = SourceFactory.getDataStream(xxx);
// Tuple2<Long, String> -> photo_id + "COMMENT"标签
DataStream<Tuple2<Long, String>> comment = SourceFactory.getDataStream(xxx);
// Tuple2<Long, String> -> photo_id + "SHARE"标签
DataStream<Tuple2<Long, String>> share = SourceFactory.getDataStream(xxx);
// Tuple2<Long, String> -> photo_id + "NEGATIVE"标签
DataStream<Tuple2<Long, String>> negative = SourceFactory.getDataStream(xxx);
// Tuple5<Long, Long, Long, Long> -> photo_id + play_cnt + like_cnt + comment_cnt + window_start_timestamp
DataStream<Tuple3<Long, Long, Long>> playAndLikeCnt = play
.union(like)
.union(comment)
.union(share)
.union(negative)
.keyBy(KeySelectorFactory.get(i -> i.f0))
.timeWindow(Time.seconds(60))
.process(xxx);
env.execute();
}
}
可以发现,无论上游数据源怎样进行变化,上述 union 方案中始终可以保持只有一个窗口算子处理和计算数据,则可以解决之前列举的数据延迟以及 flink 任务算子过多的问题。
在数据源的 schema 相同(或者不同但经过处理之后可以 format 成相同格式)的情况下,或者处理逻辑相同的话,可以使用 union 进行逻辑简化。
总结
本文首先介绍了需求场景,第二部分分析了使用 cogroup(案例代码)是如何解决此需求场景,再分析了此实现方案可能会存在一些问题,并引出了 union 解决方案的逆推和设计思路。在第三部分针对此场景使用 union 代替 cogroup 进行了一定程度上的优化。
Flink实例(五十): Operators(十)多流转换算子(五)coGroup 与union的更多相关文章
- 十天学Linux内核之第五天---有关Linux文件系统实现的问题
原文:十天学Linux内核之第五天---有关Linux文件系统实现的问题 有时间睡懒觉了,却还是五点多醒了,不过一直躺倒九点多才算起来,昨晚一直在弄飞凌的嵌入式开发板,有些问题没解决,自己电脑系统的问 ...
- 疯狂JAVA讲义---第十二章:Swing编程(五)进度条和滑动条
http://blog.csdn.net/terryzero/article/details/3797782 疯狂JAVA讲义---第十二章:Swing编程(五)进度条和滑动条 标签: swing编程 ...
- [习题]输入自己的生日(年/月/日)#2 -- 日历(Calendar)控件的时光跳跃,一次跳回五年、十年前?--TodaysDate属性、VisibleDate属性
原文出處 http://www.dotblogs.com.tw/mis2000lab/archive/2013/06/10/calendar_visibledate_birthday_dropdow ...
- (十四--十五)数据库查询优化Part I
(十四--十五)数据库查询优化Part I 如果理解的有问题.欢迎大家指出.这也是我在看课记得笔记..可能会有很多问题 查询优化的重要性 请记住用户只会告诉DMBS他们想要什么样的结果,而不会告诉他们 ...
- abp(net core)+easyui+efcore实现仓储管理系统——入库管理之十(四十六)
abp(net core)+easyui+efcore实现仓储管理系统目录 abp(net core)+easyui+efcore实现仓储管理系统——ABP总体介绍(一) abp(net core)+ ...
- C# 服务端推送,十步十分钟,从注册到推送成功
目标 展示 C# 服务端集成极光推送的步骤,多图少字,有图有真相. 使用极光推送, C# 服务端推送到 Demo App,Android 手机收到推送,整理为十个步骤,使用十分钟左右,完成从注册账号到 ...
- php 接收二进制流转换成图片
php 接收二进制流转换成图片,图片类imageUpload.php如下: <?php /** * 图片类 * @author http://blog.csdn.net/haiqiao_2010 ...
- delphi 怎么将一个文件流转换成字符串(String到流,String到文件,相互转化)
//from http://kingron.myetang.com/zsfunc0d.htm (*// 标题:充分利用pascal字符串类型 说明:和PChar不同,string可以保存# ...
- Windows五种IO模型性能分析和Linux五种IO模型性能分析
Windows五种IO模型性能分析和Linux五种IO模型性能分析 http://blog.csdn.net/jay900323/article/details/18141217 http://blo ...
随机推荐
- Java 中基本数据类型的变量的转换规则
基本数据类型之间的转换 变量之间的转换规则 布尔型变量在和其他 7 种基本数据类型做运算时,无法转化为其他的数据类型,所以下面所说的运算都是除了布尔型的其他 7 种基本数据类型之间的转换. 1.自动类 ...
- CMOS设计手册—基础篇
模拟CMOS 衬底噪声:由于相邻的电阻互相注入电流而产生的衬底噪声.解决方法:在两个电阻之间加入一个P+注入区(作为P衬底晶圆的衬底接触).P+注入区保护电路免受载流子的影响,由于注入区是一个环形,所 ...
- 三层架构的一点理解以及Dapper一对多查询
1.首先说一下自己对三层架构的一点理解 论坛里经常说会出现喜欢面相对象的写法,所以使用EF的,我个人觉得他俩没啥关系,先别反对,先听听我怎么说吧. 三层架构,基本都快说烂了,但今天还是说三层架构:UI ...
- 剑指 Offer 43. 1~n整数中1出现的次数
题目描述 输入一个整数 n ,求1-n这n个整数的十进制表示中1出现的次数. 例如,输入12,1-12这些整数中包含1 的数字有1.10.11和12,1一共出现了5次. 示例 1: 输入:n = 12 ...
- Mysql批量导入
这应该是我写Mysql技术的最后一章了吧,短时间内应该不会再写Mysql的文章了,当然疑难杂症除外 insert语句优化 因为之前我也遇到过这样的问题,是我在做数据库适配的时候碰见的,那是我的数据还是 ...
- 基于Goc的Golang代码VSCode实时染色方案
近日,Li Yiyang 老师基于Goc做了个VS Code插件,能够直观的看到被执行到的代码,当真充满想象力: 感兴趣的同学可以去goc仓库查看详情. Goc的核心能力就在于能够帮助我们在被测程序运 ...
- Spring AOP-用代理代替繁琐逻辑
Spring AOP 基础概念 AOP 是一种面向切面的编程思想,通俗来讲,这里假如我们有多个方法. @Component public class Demo { public void say1() ...
- [程序员代码面试指南]栈和队列-最大值减去最小值 小于或等于num 的子数组的数量(单调队列)
题目 给定数组arr和整数num,求数组的子数组中有多少个的满足"最大值减去最小值<=num". 解题思路 分析题目,有结论: 如果数组arr[i...j]满足条件,则它的每 ...
- [IDEA]Java:“程序包XXX不存在”问题的三种解决方案
###三种方案 ####01 出现jar包找不到的问题,首先有可能是项目依赖中有些jar没有下载完整 而mvn idea:idea这个命令可以检查并继续下载未下载完整的依赖jar. 在命令行输入mvn ...
- day52:django:ORM单表/多表操作
目录 1.ORM 2.ORM单表增删改查 13个必知必会的查询接口 filter基于双下划线的模糊查询 3.ORM多表增删改查 ORM 什么是ORM? ORM(object relational ma ...