Flink join(流流)详解(一)

【Flink知识汇总】【Join专题】 Flink Join 专题Join 的应用场景批Join和流Join的区别Flink 双流Join1. Window Join认识Window JoinFlink APISQL API解决方案的特点解决方案的适用场景2. Interval Join2.1 认识Interval Join2.2 Flink API2.3 SQL API2.4 解决方案的特点2.5 解决方案的适用场景3. Regular Join3.1 认识Regular Join3.2 SQL API3.3 解决方案的特点3.4 解决方案的适 阅读详情

本文基于flink 1.11进行测试。

前言

这里所说的join是两个或者多个流的join,涉及流批join的内容或者批批join会另写一篇文章专门说。

Flink的join按照窗口类型分可以分为:Tumbling Window Join、Sliding Window Join和Session Window Join。

按join类型分可以分为join和intervalJoin。前者类似RDBMS中的内连接,interval join使用一个公共键连接两个流的元素(我们现在称它们为A和B),其中流B的元素的时间戳与流A中元素的时间戳之间存在相对时间间隔。

inner join示意图:

interval join示意图:

 

本文会用到

代码

创建用于join的两个数据源

package it.kenn.source;

import org.apache.flink.streaming.api.functions.source.SourceFunction;
import scala.Tuple3;

import java.util.Random;

public class ForJoinSource1 implements SourceFunction<Tuple3<String, Long, Double>> {
    boolean flag = true;

    @Override
    public void run(SourceContext<Tuple3<String, Long, Double>> ctx) throws Exception {
        Random random = new Random();
        while (flag) {
            int randInt = random.nextInt(100);
            ctx.collect(new Tuple3<>("S" + randInt, System.currentTimeMillis(), random.nextDouble() * 1000));
            Thread.sleep(30);
        }
    }

    @Override
    public void cancel() {
        flag = false;
    }
}
//-----------------------------------------------------------------------------------
package it.kenn.source;

import org.apache.flink.streaming.api.functions.source.SourceFunction;
import scala.Tuple3;

import java.util.Random;

public class ForJoinSource2 implements SourceFunction<Tuple3<String, Long, Double>> {
    boolean flag = true;

    @Override
    public void run(SourceContext<Tuple3<String, Long, Double>> ctx) throws Exception {
        Random random = new Random();
        while (flag) {
            int randInt = random.nextInt(110);
            ctx.collect(new Tuple3<>("S" + randInt, System.currentTimeMillis(), random.nextDouble() * 1000));
            Thread.sleep(20);
        }
    }

    @Override
    public void cancel() {
        flag = false;
    }
}

测试主程序

package it.kenn.join;

import it.kenn.source.ForJoinSource1;
import it.kenn.source.ForJoinSource2;
import org.apache.flink.api.common.eventtime.WatermarkStrategy;
import org.apache.flink.api.common.functions.JoinFunction;
import org.apache.flink.streaming.api.TimeCharacteristic;
import org.apache.flink.streaming.api.datastream.DataStream;
import org.apache.flink.streaming.api.datastream.SingleOutputStreamOperator;
import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;
import org.apache.flink.streaming.api.functions.co.ProcessJoinFunction;
import org.apache.flink.streaming.api.windowing.assigners.SlidingEventTimeWindows;
import org.apache.flink.streaming.api.windowing.assigners.TumblingEventTimeWindows;
import org.apache.flink.streaming.api.windowing.time.Time;
import org.apache.flink.util.Collector;
import scala.Tuple3;

import java.time.Duration;

/**
 * 测试join
 */
//测试主程序
public static void main(String[] args) throws Exception {
        StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
        StreamStreamJoinTest joinTest = new StreamStreamJoinTest();
        //设置事件时间
        env.setStreamTimeCharacteristic(TimeCharacteristic.EventTime);
        DataStream<Tuple3<String, Long, Double>> source30 = env.addSource(new ForJoinSource1())
                //指定时间戳和watermark规则,注意要指定两个:forBoundedOutOfOrderness指定watermark生成策略,withTimestampAssigner指定那个字段是事件时间
                .assignTimestampsAndWatermarks(WatermarkStrategy.<Tuple3<String, Long, Double>>forBoundedOutOfOrderness(Duration.ofMillis(10)).withTimestampAssigner((e, ts) -> e._2()));
        DataStream<Tuple3<String, Long, Double>> source20 = env.addSource(new ForJoinSource2())
                .assignTimestampsAndWatermarks(WatermarkStrategy.<Tuple3<String, Long, Double>>forBoundedOutOfOrderness(Duration.ofMillis(10)).withTimestampAssigner((e, ts) -> e._2()));
        DataStream<Tuple3<String, Long, Double>> tumbJoinedStream = joinTest.tumbJoin(source20, source30);
        DataStream<Tuple3<String, Long, Double>> slidingJoinStream = joinTest.slidingJoin(source20, source30);
        DataStream<Tuple3<String, Long, Double>> intervalJoinStream = joinTest.intervalJoin(source20, source30);
        //对不同join进行测试
        tumbJoinedStream.print();
        env.execute();
    }

inner Tumbling Window Join代码测试

/**
     * inner Tumbling Window Join
     *
     * @param source20
     * @param source30
     * @return
     */
    public DataStream<Tuple3<String, Long, Double>> tumbJoin(DataStream<Tuple3<String, Long, Double>> source20, DataStream<Tuple3<String, Long, Double>> source30) {
        DataStream<Tuple3<String, Long, Double>> joinedStream = source20.join(source30)
                .where(e -> e._1().split("-")[1])//左流要join的字段
                .equalTo(e -> e._1().split("-")[1])//右侧流要join的字段
                .window(TumblingEventTimeWindows.of(Time.milliseconds(50)))//指定窗口类型和窗口大小
                //join函数,这里说是join但是跟数据库的join有一些区别,比如下面的逻辑并没有取两个流中的数据,而是比较两个流中数据的大小,只返回某个流中的数据
                .apply(new JoinFunction<Tuple3<String, Long, Double>, Tuple3<String, Long, Double>, Tuple3<String, Long, Double>>() {
                    @Override
                    public Tuple3<String, Long, Double> join(Tuple3<String, Long, Double> left, Tuple3<String, Long, Double> right) throws Exception {
                        return left._3() > right._3() ? left : right;
                    }
                });
        return joinedStream;
    }

下图是tumble window join的示意图,但是下面join结果有些歧义,像是笛卡尔积。其实只要在join的时候加上where条件就不可能会产生下面笛卡尔积的情况了。

下图还有一个信息点,在最后一个窗口的时候,只有橙色流中有数据,绿色流中并没有数据,那么这个窗口的计算不会被触发。

 

sliding Join 测试

/**
     * sliding Join 测试
     *
     * @param source20
     * @param source30
     * @return
     */
    public DataStream<Tuple3<String, Long, Double>> slidingJoin(DataStream<Tuple3<String, Long, Double>> source20, DataStream<Tuple3<String, Long, Double>> source30) {
        DataStream<Tuple3<String, Long, Double>> joinedStream = source20.join(source30)
                .where(e -> e._1().split("-")[1])//左流要join的字段
                .equalTo(e -> e._1().split("-")[1])//右侧流要join的字段
                .window(SlidingEventTimeWindows.of(Time.milliseconds(50), Time.milliseconds(30)))//指定窗口类型和窗口大小
                .apply(new JoinFunction<Tuple3<String, Long, Double>, Tuple3<String, Long, Double>, Tuple3<String, Long, Double>>() {
                    @Override
                    public Tuple3<String, Long, Double> join(Tuple3<String, Long, Double> left, Tuple3<String, Long, Double> right) throws Exception {
                        return left._3() > right._3() ? left : right;
                    }
                });
        return joinedStream;
    }

sliding join示意图:

interval join有一个需要注意的特点:有些事件可能在一个滑动窗口中没有被join但是在另外一个滑动窗口中去呗join了。比如上图橙色2号事件,在蓝色窗口中没有与绿色流join,但是在后面的绿色窗口中却与绿色3号join了。

 

Session Window Join测试

这种方式用到的场景好像不太多,如果哪天我用到了会在这里补上笔记的。

import org.apache.flink.api.java.functions.KeySelector;
import org.apache.flink.streaming.api.windowing.assigners.EventTimeSessionWindows;
import org.apache.flink.streaming.api.windowing.time.Time;
 
...

DataStream<Integer> orangeStream = ...
DataStream<Integer> greenStream = ...

orangeStream.join(greenStream)
    .where(<KeySelector>)
    .equalTo(<KeySelector>)
    //指定Gap大小
    .window(EventTimeSessionWindows.withGap(Time.milliseconds(1)))
    .apply (new JoinFunction<Integer, Integer, String> (){
        @Override
        public String join(Integer first, Integer second) {
            return first + "," + second;
        }
    });

session window join示意图:

上图是session window示意图。可以看到他的原理是通过两个流的间隔时间划分的窗口,这种窗口的数量非常不稳定。如果流中event间隔一直小于指定的GAP,那么窗口会一直不触发。换句话说,这种窗口的触发相比其他窗口而言比较被动,完全是数据驱动的触发,而不是时间驱动的触发。

 

interval Join 测试

/**
     * interval Join 测试
     *
     * @param source20
     * @param source30
     * @return
     */
    public DataStream<Tuple3<String, Long, Double>> intervalJoin(DataStream<Tuple3<String, Long, Double>> source20, DataStream<Tuple3<String, Long, Double>> source30) {
        SingleOutputStreamOperator<Tuple3<String, Long, Double>> intervalJoinedStream = source20.keyBy(e -> e._1().split("-")[1])
                .intervalJoin(source30.keyBy(e -> e._1().split("-")[1]))
                .between(Time.milliseconds(-12), Time.milliseconds(9))
                //默认情况下上面的between条件是包含边界的,如果不希望包含边界可以使用下面两个方法去除
                .lowerBoundExclusive()
                .upperBoundExclusive()
                .process(new ProcessJoinFunction<Tuple3<String, Long, Double>, Tuple3<String, Long, Double>, Tuple3<String, Long, Double>>() {
                    @Override
                    public void processElement(Tuple3<String, Long, Double> left, Tuple3<String, Long, Double> right, Context ctx, Collector<Tuple3<String, Long, Double>> out) throws Exception {
                        out.collect(left._3() > right._3() ? left : right);
                    }
                });
        return intervalJoinedStream;
    }

interval join示意图:

上图是interval join示意图。之前遇到过一个场景。假设绿色流和黄色流是两组人的运动轨迹。在黄色2位置,某人进了一家餐馆,求跟黄色2号事件前后5分钟同时进入这家餐馆的绿色事件,使用interval join就很合适。

还有一点需要注意,在上面注释也写明了,图中也画出来了,默认情况下between是包含边界的,如果要去掉边界,需要使用上面两个函数去除边界,当然可以根据情况只去除一个边界。

 

 

 

Flink 双流Join 对处于同一窗口的数据进行join时间类型:processTime、eventTime问题:1、不在同一窗口的数据无法join,2、只能inner join。 阅读详情

相关推荐

Flink双流JOIN

Flink双流JOIN

大数据知识梳理 1万+

Flink 第8.4章 Flink Join 精讲

序 参考: Flink 中文社区 预计阅读时间:15min,共5700字。 Join Join 语义以及实现现在有很多的成熟方案,然而近年来,实时流之间的Join 却是刚刚起步。 在 Join 之中,最重要的就是我们一般实现 join 需要依赖于缓存整个数据集,但是在实时数据中,他是一个无限的数据流,内存压力和计算效率在长时间运行时,都会带来不可避免的问题。 那么我们先讲讲对于 Join的基础知识。 Batch SQL Join 传统的离线Batch SQL 有三种基础的...

青冬的博客 4868

解读Flink双流Join

基本概念 双流Join是Flink面试的高频问题。一般情况下说明以下几点基本就可以cover了: Join大体分类只有两种:Window Join和Interval Join。Window Join又可以根据Window的类型细分出3种: Tumbling Window Join、Sliding Window Join、Session Widnow Join。 Windows类型的join都是利...

石权的博客 1万+

Flink join详解(含两类API及coGroup、connect详解)

join、regular join、interval join、temporal join、connect、coGroup

weixin_51981189的博客 4795

Flink的Look Up Join详解

- 定义主流订单表(从 Kafka/Socket 等)) WITH ('connector' = 'kafka', -- 示例,这里仅做说明-- 定义 MySQL 维表) WITH ('lookup.cache.max-rows' = '5000', -- 启用缓存,最多缓存 5000 条'lookup.cache.ttl' = '60s', -- 缓存条目的 TTL 为 60 秒'lookup.max-retries' = '3' -- 遇到网络或其他错误时最多重试 3 次。

weixin_50227709的博客 2882

从零开始学Flink:Flink 双流 JOIN 实战详解

通过订单与支付双流关联的实战案例,系统讲解 Flink SQL 中的双流 JOIN 类型、时间条件与实现方式,帮助你理解流计算场景下的关联查询思路和坑点。

专注大数据与 Java 全栈技术栈。深耕 Flink 实时计算、Spring Boot 源码解析与高并发系统架构设计。拒绝浅尝辄止,坚持源码级剖析与生产环境实战复盘,致力于为开发者提供有深度的技术干货与架构思维。代码不止,匠心不息。 1914

十 Flink实现双流join

文章目录1 基于时间的双流join1.1 用法:1.2 案例2 基于窗口做双流join2.1 案例:3 使用connect 和 CoProcessFunction实现join 1 基于时间的双流join 基于间隔的 Join 会对两条流中拥有相同键值以及彼此之间时间戳不超过某一指定间隔的事件进行 Join。 基于时间间隔的 Join 目前只支持事件时间以及 INNER JOIN 语义 1.1 用法: input1 .keyBy(...) .between(<lower-bound>, <u

andy的博客 9343

flink中 Lookup Join和Interval Join和Regular Join,Temporal Join使用场景与对比

Flink中三种Join对比:1)Lookup Join用于流表关联外部维表(如MySQL),无状态但依赖外部查询性能;2)Interval Join通过时间窗口关联双流(如1小时内订单匹配支付),状态仅保留窗口数据;3)Regular Join实现无限制双流关联,状态持续增长需设置TTL。选型建议:查维表用Lookup,时间窗用Interval,无限制精确匹配用Regular。

努力工作学习的程序员 1237

Flink中的双流Join详解

1-Flink中双流Join介绍 注意:双流Join是Flink面试的高频问题 Join大体分类只有两种:Window Join和Interval Join。 Window Join又可以根据Window的类型细分出3种: Tumbling Window Join、Sliding Window Join、Session Widnow Join。 Windows类型的join都是利用window的机制,先将数据缓存在Window State中,当窗口触发计算时,执行join操作; interval joi

LBJ_小松鼠的博客 5858

Flink双流Join操作:Window Join和Interval Join详解

在实时数据处理的世界里,“关联”是最常见的需求之一——就像电商平台需要将“订单流”与“支付流”关联以确认交易完成,物流系统需要将“订单流”与“物流流”关联以追踪包裹位置。(窗口Join)和(区间Join)。本文将用“约会”与“等待快递”的生活化比喻,拆解这两种Join的底层逻辑;通过一步步推理解析其技术原理,结合代码示例与流程图展示实现细节;最后通过实际案例与常见问题,帮助你掌握“何时用Window Join?何时用Interval Join?”的决策逻辑。

AI Python 编程之道的博客 1195

flink实战--双流join之原理解析

JOIN简介 谈flink双流JOIN之前,我们先谈一下大家最熟悉的mysql表join,我们知道表join有如下几种,具体区别就不在介绍了。那么流的join和表的join有什么区别呢?本文我们介绍一下。 CROSS JOIN - 交叉连接,计算笛卡儿积; INNER JOIN - 内连接,返回满足条件的记录; OUTER JOIN LEFT - 返回左表所有行,右表不存...

阿华田的博客 9330

Flink双流join

Flink双流JOIN是指将两个独立的数据流按照关联条件进行实时匹配连接的操作,其核心挑战在于处理无限数据流和乱序事件15。

code_fly 1126

Opencv计算相机响应函数(Camera Response Function)与Radiance

本文主要用于记录自己在学习CRF过程中遇到的一些问题。 CRF模型 I=f(V(t⋅L))I=f(V( t\cdot L))I=f(V(t⋅L)) 其中III为相机灰度值Intensity,LLL为环境中irradiance,ttt为曝光时间,VVV为相机镜头参数,fff为相机响应函数CRF。 有些论文中认为相机镜头参数是线性的不考虑,但有些考虑了衰减即靠近图像边缘亮度会暗一些,但没有看到二者同时计算的,如果同时计算两个非线性的函数,我觉得可能会有不确定性,在这里还请教一下了解的朋友。 控制

larrydong的博客 5997

Flink 窗口 Join 与区间 Join 实战详解

本文介绍了Flink中基于事件时间的流数据Join操作,重点讲解了窗口Join和区间Join的实现方法。首先明确了使用事件时间和Watermark策略处理乱序数据的基础设定。然后详细阐述了三种窗口Join:1) 滚动窗口Join,固定长度无重叠,适用于精确对齐场景;2) 滑动窗口Join,可重叠窗口,提高匹配率但可能重复输出;3) 会话窗口Join,动态长度窗口,适用于非连续事件场景。此外还介绍了区间Join,通过时间范围条件实现更灵活的关联。文中提供了完整的Java代码示例,包括事件定义、Watermar

hello.reader 8013

基于FME—三调篇—尖锐角及小缝隙处理

1 问题分析: 该功能实现在FME较为简单,相对ARCGIS模型构建器以及ARCGIS ENGINE,FME更适合无代码基础的人群。在三调内业数据处理要求中,尖锐角是不允许存在的。本文基于FME实现了对尖锐角的自动查询与消除以及自动处理在消除尖锐角过程中产生的缝隙,并进行了拓扑检查,实现自动化批量处理尖锐角的功能,提高工作效率。 2 总体思路: 2.1 实现功能分析: 1.找出小于指定角度的尖锐角...

qq_33315579的博客 9179

波克 上海 Unity开发笔试题 2024

主要分为四部分,如果没有准备好,不要点击笔试链接,进入就会自动开始。

z2014z的博客 3392

Flink SQL Join 从 Regular Join 到 Temporal Join 的实战

Lookup Join 基本可以视为“处理时间时态 Join + 外部维表 Connector”。对一个不断变化的维度表,在某个给定时间点回放出“当时的版本”,并和事实流 Join。实时行为/订单流 + MySQL/HBase/Redis 中的维度表,做字段补全。“订单和支付/发货根据时间做关联,只允许在一个时间区间内 Join。左表每一行作为参数,调用一次表函数,将返回的多行和这行 Join。订单按下单时刻的汇率换算成 USD,而不是按当前最新汇率。,如果直接按写的顺序算,很有可能中间状态巨大。

hello.reader 1386

使用FloodRisk2插件进行洪水风险评估

入汛以来,长江流域持续的强降雨使得沿岸多地成为洪水风险极高地区,抗洪、抢险、救灾继新冠病毒之后成为又一个攻坚阵地。如何科学地评估洪水风险,是快速决策的关键。 实际生活中,根据风险是否可控,一般将洪水风险大致分为两类:一类是通过数据分析可以得出其发生的可能性和后果,如强降雨引发的区域性洪水、水库/大坝溃决洪水等。这类洪水可以通过有关资料分析和设定一定条件计算得到可能的损失后果,也就是说,风险可以预判。另一类洪水风险基本是未知的,如滑坡堵塞河道形成堰塞湖引发的洪水,无法预知事件发生的地点和规模,具体的洪水风险无

QGISClass的博客 2877

flink 多流join 触发时机详解

flink 多流join 触发时机详解 flink多流join代码很简单,但是对于初学者可能会遇到window窗口计算不能触发的"假象",这往往是由于对flink window eventtime processtime理解不到位引起的,以下示例将详述join在不同时间下的触发过程...

5145

【2026-Spark实验一-Windows中安装Spark环境】

本文详细介绍了在Windows系统中配置Spark 3.5.8开发环境的完整流程。主要内容包括:安装JDK 8并配置环境变量,下载并解压Spark 3.5.8预编译版,设置SPARK_HOME系统变量,配置Hadoop的winutils工具解决Windows兼容性问题。通过spark-shell和SparkPi示例程序验证环境配置成功,确保Spark能够正常运行分布式计算任务。文中特别强调了路径命名规范、环境变量设置顺序等关键细节,提供了完整的配置截图和验证方法,帮助开发者快速搭建稳定的Spark学习环境。

pblh123的专栏 1586

Halcon那些事:Halcon缺陷检测检测印刷数字完整性

创建模板 create_shape_model模板匹配 find_shape_model。

十多年IT经验,专注机器视觉,智能制造半导体行业 764

Flink SQL Regular Join 、Interval Join、Temporal Join、Lookup Join 详解

Flink SQL Regular Join 、Interval Join、Temporal Join、Lookup Join 详解

猫猫爱吃小鱼粮 2864

超越防抖:索尼CXD5254GG芯片如何重新定义电子影像稳定

索尼CXD5254GG芯片通过融合6轴IMU与图像传感器数据,重新定义了电子影像稳定技术。其核心突破在于实时地平线校正、抑制果冻效应和镜头畸变校正,并搭载智能画质控制器提升画面细节。该芯片适用于广播制作、体育娱乐、安防监控和机器人视觉导航等专业领域,提供开箱即用的优化参数配置,大幅缩短开发周期。CXD5254GG不仅实现稳定成像,更通过姿态感知与智能优化,为专业影像应用提供了完整的稳定解决方案。

致力于lidar应用开发 112

Flink join终结者:SQL Join

点击上方蓝 字关注~ SQL是开发人员与数据分析师必备的技能,Flink也提供了Sql方式编写任务,能够很大程度降低开发运维成本,这篇是flink join的终极篇SQL Join, 首先介绍sql join使用方式、然后介绍global join带来的状态存储成本及解决方式、最后从源码角度分析sql join实现。 一、SQL JOIN使...

张普的专栏 3732

单稳态触发器Multisim电路仿真——硬件工程师笔记

本文系统介绍了单稳态触发器的基础知识及其实现方式。主要内容包括:1)单稳态触发器的工作原理、电路结构、特点及应用;2)555定时器实现单稳态触发器的配置及工作过程;3)使用反相器和与非门实现积分型和微分型单稳态触发器的电路结构及工作原理。文章详细阐述了各种实现方式的特性、应用场景及设计考虑,为硬件工程师提供了实用的电路设计参考。最后还附有相关笔试面试题链接,便于读者进一步学习扩展。

XU157303764的博客 1875

Devexpress16.2.6破解工具

Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具

上一篇: Flink window function及常用窗口算子
下一篇: Flink process function使用详解
iFence
博客等级 码龄9年 8051粉丝 111原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值