flink数据广播场景总结
数据集广播,主要分为广播变量,广播维表(数据集)两种,一种为变量,一种为常量(抽象的说法);
一.数据广播背景
对于小变量,小数据集,需要和大数据集,大流进行联合计算的时候,往往把小数据集广播出去,整体直接和大数据集(流)的分布式最小粒度数据进行计算,最后把计算结果合并,这样效率更高,省去分布式节点之间的数据传输及二次计算。
例如:在Flink使用场景中,外部的配置文件或计算规则及维表等进行预加载,并定期更新,流式计算中广播小变量等场景。
数据集的广播,主要有以下几种方式可以实现
1.预加载
在算子的open()方法中读取MySQL或其他存储介质,获取全量维表信息比如在算子RichMapFunction的open()方法中获取全部数据,然后在算子中进行使用,这种方法的缺点是如果外部数据更新了Flink是没法知道的,这就需要在开启一个定时任务定时从MySQL中获取最新的数据。
2.外部查询
数据不需要存储,仅需要用到外部数据的时候去进行查询,可以保证查询到的数据是最新的,但是对于吞吐量较高的场景,可能与外部(比如MySQL)交互就变成了 Flink任务的瓶颈,虽然可以设置为异步I/O的形式进行交互优化,但优化程度一般有限。
3.本地缓存
需要设置过期时间或者定时更新数据,当数据到达过期时间后从新从外部获取,或者定时从外部捞取数据进行更新,不能在外部数据发生变动时,及时更新到Flink程序中。
预加载和本地缓存难以应对当外部数据发生变化时,数据实时在Flink中保持更新。
二.什么是广播
类似于全局性共享的数据,详见官方文档
https://flink.apache.org/2019/06/26/broadcast-state.html
https://ci.apache.org/projects/flink/flink-docs-release-1.12/zh/dev/stream/state/broadcast_state.html
广播的优势
广播变量创建后,它可以运行在集群中的任何function上,而不需要多次传递给集群节点,可以直接在内存中拿数据,避免了大量的shuffle,导致集群性能下降。我们可以把一个dataset或者不变的缓存对象(例如maplist集合对象等)数据集广播出去,然后不同的任务在节点上都能够获取到,并在每个节点上只会存在一份,而不是在每个并发线程中存在。
如果不使用broadcast,则在每个节点中的每个任务中都需要拷贝一份dataset数据集,比较浪费内存(也就是一个节点中可能会存在多份dataset数据)。广播变量,可以借助下图辅助理解。
三.广播的使用
根据广播使用场景将广播的类型分为广播变量和广播流(其实广播原理是一样的)。
1.广播变量
将广播的数据作为一个整体或对象广播,比如从MySQL中一次获取全部数据,然后广播出去,因为数据在MySql中,如果MySql中某条记录发生变动,Flink的souce是没法知道,也不会广播。所以只能在souce中定时从MySql中获取全部数据,然后广播更新。
示例数据格式:
kafka源流数据,只有itemid,没有ip和port
{"host":"orcl", "itemid":"7875", "value":1}
{"host":"orcl2", "itemid":"7876", "value":2}
规则数据集在MySql,itemid关联ip和port
itemid ip port
7875 192.168.199.105 1521
7876 192.168.199.106 1526
自定义MySql source, 定时 从Mysql获取全部数据
@Override
public void run(SourceContext<HashMap<String, Tuple2<String, Integer>>> ctx) {
try {
while (isRunning) {
HashMap<String, Tuple2<String, Integer>> output = new HashMap<>();
ResultSet resultSet = preparedStatement.executeQuery();
//每隔60s获取全部外部数据集
while (resultSet.next()) {
String itemid = resultSet.getString("itemid");
String ip = resultSet.getString("ip");
int port = resultSet.getInt("port");
output.put(itemid, new Tuple2<>(ip, port));
}
ctx.collect(output);
Thread.sleep(1000 * 60);
}
} catch (Exception ex) {
log.error("从Mysql获取配置异常...", ex);
}
}
广播代码实现:
public void processElement(Map<String,Object> value, ReadOnlyContext ctx, Collector<Map<String,Object>> out) throws Exception {
//从广播中获取全量数据
ReadOnlyBroadcastState<Void, Map<String, Tuple2<String, Integer>>> broadcastState = ctx.getBroadcastState(ruleStateDescriptor);
//获取全部规则数据进行匹配
Map<String, Tuple2<String, Integer>> itemrules= broadcastState.get(null);
//规则数据集为空跳过
if(itemrules==null) {
return;
}
//事件流中的itemid
Object itemidObj = value.get("itemid"); // value kafka流中数据获取itemid
if (itemidObj == null) {
return;
}
Tuple2<String, Integer> itemruld = itemrules.get(itemidObj.toString());
if(itemruld!=null){
//匹配成功增加ip,port字段
value.put("ip", itemruld.f0);
value.put("port", itemruld.f1);
out.collect(value);
}
}
@Override
public void processBroadcastElement(HashMap<String, Tuple2<String, Integer>> value, Context ctx, Collector<Map<String,Object>> out) throws Exception {
//数据全部更新
BroadcastState<Void, Map<String, Tuple2<String, Integer>>> broadcastState = ctx
.getBroadcastState(ruleStateDescriptor);
//每次更新全部规则数据
broadcastState.put(null, value);
System.out.println("规则全部更新成功,更新item规则:" + value);
}
执行结果:
//itemid=7875关联ip=192.168.199.104
{host=orcl, itemid=7875, value=1, ip=192.168.199.104, port=1521}
//手动将mysql中的ip=192.168.199.104改为ip=192.168.199.105,在source 休眠结束后将会更新数据
规则更新成功,更新item规则:{7875=(192.168.199.105,1521), 7876=(192.168.199.106,1526)}
//itemid=7875关联ip=192.168.199.105
{host=orcl, itemid=7875, value=1, ip=192.168.199.105, port=1521}
这种方式和预加载很像,都是通过定时任务加载全部数据,只不过是方法的位置不同,一个是在自定义source中设置休眠时间,另外一个是在算子的open方法中设置定时任务,广播变量的方式同样无法做到数据修改后实时更新。
2.广播流
当数据来源于kafka时,Flink消费kafka获取流,将流数据存储在广播状态中,称之为广播流,不同于广播变量一次获取全部数据,广播流是kafka新增一条记录就将这条记录存储到广播中,那广播流如何实现外部数据的新增和更新?
kafka源流数据,只有itemid,没有ip和port
{"host":"orcl", "itemid":"7875", "value":1}
{"host":"orcl2", "itemid":"7876", "value":2}
规则数据集在kafka,itemid关联ip和port
{"itemid":"7875","ip":"192.168.199.104","port":1521}
{"itemid":"7876","ip":"192.168.199.106","port":1526}
2.1 外部数据新增和修改记录
// 广播状态底层结构是Map结构
//kafka中的数据,flink消费后存储到广播状态,在广播状态中以itemid为key进行存储
{"itemid":"7875","ip":"192.168.199.104","port":1521}
{"itemid":"7876","ip":"192.168.199.106","port":1526}
//新增 往kafka写入新记录(key不相同),flink会持续消费kafka并将数据通过Map的put()方法存入广播状态
//修改 往kafka写入新记录(key相同),put()方法覆盖之前的这条记录以达到更新的目的
//比如需要更新itemid的ip和port值,要求往kafka中写入一条新数据,比如更新itemid 7875的ip和port
{"itemid":"7875","ip":"192.168.199.105","port":1525}
2.2 删除记录
//kafka中的数据,flink消费后存储到广播状态,以itemid为key进行存储
{"itemid":"7875","ip":"192.168.199.104","port":1521}
{"itemid":"7876","ip":"192.168.199.106","port":1526}
//如果需要删除某条记录,往kafka中写入带有key的数据和删除标记即可
//比如删除itemid为7875的记录,要求往kafka中写入一条新数据,程序删除广播中itemid7875的记录
{"itemid":"7875","isRemove":true}
由于消费kafka流是实时的,kafka的新记录会实时进行消费,根据新记录的内容对广播数据实时的进行新增,修改或删除
同时由于kafka中的数据是不可变的,当程序需要重启时,只需从头消费kafka即可,由于具有幂等性,最终的广播数据是不会变的。
示例代码
//Flink消费外部kafka规则数据作为流
FlinkKafkaConsumer<ItemRuleEntiy> ruleKafkaConsumer = new FlinkKafkaConsumer<ItemRuleEntiy>("topic",new ItemRuleEntiyPojoSchema(),properties);
DataStream<ItemRuleEntiy> ruleStream = env.addSource(ruleKafkaConsumer);
//广播方法
@Override
public void processBroadcastElement(ItemRuleEntiy value,
BroadcastProcessFunction<Map<String, Object>, ItemRuleEntiy, Map<String, Object>>.Context ctx,
Collector<Map<String, Object>> out) throws Exception {
BroadcastState<String, ItemRuleEntiy> broadcastState = ctx.getBroadcastState(ruleStateDescriptor);
if (StringUtils.isNoneBlank(value.getItemid())) {
System.out.println("获取到新的广播规则:" + value);
//相比广播变量,这里每次只存一条规则,相同key则覆盖修改
broadcastState.put(value.getItemid(), value); // 存放数据到广播
}
}
@Override
public void processElement(Map<String, Object> value,
BroadcastProcessFunction<Map<String, Object>, ItemRuleEntiy, Map<String, Object>>.ReadOnlyContext ctx,
Collector<Map<String, Object>> out) throws Exception {
ReadOnlyBroadcastState<String, ItemRuleEntiy> broadcastState = ctx.getBroadcastState(ruleStateDescriptor);
Object itemidObj = value.get("itemid"); // 源kafka流中数据获取itemid
if (itemidObj == null) {
return;
}
// 根据item从广播数据中查找规则,能查到,则增加ip,port字段
ItemRuleEntiy itemRule = broadcastState.get(itemidObj.toString());
if (itemRule != null) { // 从广播中捞取到数据时
value.put("ip", itemRule.getIp());
value.put("port", itemRule.getPort());
out.collect(value);
}
}
执行结果
// 所有广播规则数据:
7875={itemid=7875, ip=192.168.199.104, port=1521}
7876={itemid=7876, ip=192.168.199.106, port=1526}
//itemid=7875关联ip=192.168.199.104
({host=orcl, itemid=7875,value=1, ip=192.168.199.104, port=1521},7875)
//kafka写入{itemid=7875, ip=192.168.199.105, port=1521}
获取到新的广播规则:{itemid=7875, ip=192.168.199.105, port=1521}
//itemid=7875关联ip=192.168.199.105
{host=orcl,itemid=7875, value=1, ip=192.168.199.105, port=1521},7875)
四.总结
通过kafka广播流的方式最终实现了Flink与外部数据交互的实时更新,不仅是kafka,还有MQ,甚至文件格式都可以作为广播流,广播流要求数据不能从内部更改(无法作为流消息被实时消费),只能通过新增的方式进行修改和删除(新增记录中key相同的表示覆盖修改,带key和删除标记的表示删除)
相比表(mysql,oracle)只是结果的呈现,日志(kafka或其它队列)是一种带有时间维度(或先后顺序)信息的存储,可以说表是二维的,日志是三维的,通过日志可以复原每个时间点的表,但是表不能还原日志。
广播作为一种流(流明显带有时间特性),所以当不带时间维度的表作为流时,是没法形成真正意义上的流,只能通过定时获取表的全部数据作为伪流,流中每个时间点的数据也只能是全量表数据,同时定时也就没法做到实时获取。只有带时间维度的日志作为流时,才能做到实时获取,而且每次只获取最新的一条记录即可,不用每次获取全部数据。
flink数据广播场景总结的更多相关文章
- Flink应用场景
本文为<Flink大数据项目实战>学习笔记,想通过视频系统学习Flink这个最火爆的大数据计算框架的同学,推荐学习课程: Flink大数据项目实战:http://t.cn/EJtKhaz ...
- 不仅仅是双11大屏—Flink应用场景介绍
双11大屏 每年天猫双十一购物节,都会有一块巨大的实时作战大屏,展现当前的销售情况. 这种炫酷的页面背后,其实有着非常强大的技术支撑,而这种场景其实就是实时报表分析. 实时报表分析是近年来很多公司采用 ...
- Flink学习笔记-新一代Flink计算引擎
说明:本文为<Flink大数据项目实战>学习笔记,想通过视频系统学习Flink这个最火爆的大数据计算框架的同学,推荐学习课程: Flink大数据项目实战:http://t.cn/EJtKh ...
- Flink学习笔记:Connectors之kafka
本文为<Flink大数据项目实战>学习笔记,想通过视频系统学习Flink这个最火爆的大数据计算框架的同学,推荐学习课程: Flink大数据项目实战:http://t.cn/EJtKhaz ...
- 大数据“重磅炸弹”——实时计算框架 Flink
Flink 学习 项目地址:https://github.com/zhisheng17/flink-learning/ 博客:http://www.54tianzhisheng.cn/tags/Fli ...
- Flink 灵魂两百问,这谁顶得住?
Flink 学习 https://github.com/zhisheng17/flink-learning 麻烦路过的各位亲给这个项目点个 star,太不易了,写了这么多,算是对我坚持下来的一种鼓励吧 ...
- Storm VS Flink ——性能对比
1.背景 Apache Flink 和 Apache Storm 是当前业界广泛使用的两个分布式实时计算框架.其中 Apache Storm(以下简称"Storm")在美团点评实时 ...
- Flink入门介绍
什么是Flink Apache Flink是一个分布式大数据处理引擎,可以对有限数据流和无限数据流进行有状态计算.可部署在各种集群环境,对各种大小的数据规模进行快速计算. Flink特性 支持高吞吐. ...
- Flink 笔记(一)
简介 Flink是一个低延迟.高吞吐.统一的大数据计算引擎, Flink的计算平台可以实现毫秒级的延迟情况下,每秒钟处理上亿次的消息或者事件. 同时Flink提供了一个Exactly-once的一致性 ...
随机推荐
- Python应用与实践-转自(吴秦(Tyler))
1. Python是什么? 1.1. Python语言 1.2. Python哲学 2. Python在工作中的应用 2.1. 实例1:文件批量处理 ...
- python基础之面向对象(一)(概念、实例、魔法方法)
一.面向对象概念理解 1.面向对象和面向过程 面向过程:核心过程二字,过程即解决问题的步骤,就是先干什么后干什么 基于该思想写程序就好比在这是一条流水线,是一种机械式的思维方式 优点:复杂的过程流程化 ...
- springboot项目部署docker服务器提供api
1.先将springboot项目打包,我这里用的是IDEA工具打包,打包完成后的jar包在 项目目录/target 中 2.打包完成后进入服务器器终端,将jar包上传到自己设置的目录中,这个目录需要跟 ...
- .Net RabbitMQ实战指南——RabbitMQ相关概念介绍
什么是消息中间件 消息(Message)是指在应用间传送的数据.消息可以非常简单,比如只包含文本字符串.JSON等,也可以很复杂,比如内嵌对象. 消息队列中间件(Message Queue Middl ...
- Go语言的函数05---匿名函数
package main import ( "fmt" "time" ) //延时执行一个匿名函数 func main071() { fmt.Println(& ...
- Java中Map<Key, Value>存储结构根据值排序(sort by values)
需求:Map<key, value>中可以根据key, value 进行排序,由于 key 都是唯一的,可以很方便的进行比较操作,但是每个key 对应的value不是唯一的,有可能出现多个 ...
- 摄像头 ISP 调试的入门之谈(经验总结)
在讲述本文之前,我尽量以一个什么也不清楚的初学到入门的用词来阐述什么是 ISP 调试,以及为什么需要调试. 如果你从来都没有接触过什么是摄像头 ISP 调试,我想这个文章可以给你一些启发和关键词. 因 ...
- GPU编程和流式多处理器(三)
GPU编程和流式多处理器(三) 3. Floating-Point Support 快速的本机浮点硬件是GPU的存在理由,并且在许多方面,它们在浮点实现方面都等于或优于CPU.全速支持异常可以根据每条 ...
- 『言善信』Fiddler工具 — 8、Fiddler检查器(Inspectors)详解
目录 1.请求报文内容 2.响应报文内容 3.响应报文中Transformer选项说明 Inspectors意思是检查器.Inspectors可以使用多种方式,查看请求的请求报文和响应报文相关信息. ...
- Java面试必知必会(扩展)——Java基础
float f=3.4;是否正确? 不正确 3.4是双精度,将双精度赋值给浮点型属于向下转型,会造成精度损失: 因此需要强制类型转换: 方式一:float f=(float)3.4 方式二:float ...