Flink中的双流Join详解

1-Flink中双流Join介绍

在这里插入图片描述
注意:双流Join是Flink面试的高频问题

Join大体分类只有两种:Window JoinInterval Join

  • Window Join又可以根据Window的类型细分出3种:
    Tumbling Window Join、Sliding Window Join、Session Widnow Join。
    Windows类型的join都是利用window的机制,先将数据缓存在Window State中,当窗口触发计算时,执行join操作;
  • interval join也是利用state存储数据再处理,区别在于state中的数据有失效机制,依靠数据触发数据清理;所以实际开发注意状态的过期时间,免得关联不到数据
    目前Stream join的结果是数据的笛卡尔积

2- Window Join

  • Tumbling Window Join
    执行翻滚窗口联接时,具有公共键和公共翻滚窗口的所有元素将作为成对组合联接,并传递给JoinFunction或FlatJoinFunction。因为它的行为类似于内部连接,所以一个流中的元素在其滚动窗口中没有来自另一个流的元素,因此不会被发射!
    如图所示,我们定义了一个大小为2毫秒的翻滚窗口,结果窗口的形式为[0,1]、[2,3]、。。。。该图显示了每个窗口中所有元素的成对组合,这些元素将传递给JoinFunction。注意,在翻滚窗口[6,7]中没有发射任何东西,因为绿色流中不存在与橙色元素⑥和⑦结合的元素。
    在这里插入图片描述
    在这里插入图片描述
  • Sliding Window Join
    在执行滑动窗口联接时,具有公共键和公共滑动窗口的所有元素将作为成对组合联接,并传递给JoinFunction或FlatJoinFunction。在当前滑动窗口中,一个流的元素没有来自另一个流的元素,则不会发射!请注意,某些元素可能会连接到一个滑动窗口中,但不会连接到另一个滑动窗口中!
    在本例中,我们使用大小为2毫秒的滑动窗口,并将其滑动1毫秒,从而产生滑动窗口[-1,0],[0,1],[1,2],[2,3]…。x轴下方的连接元素是传递给每个滑动窗口的JoinFunction的元素。在这里,您还可以看到,例如,在窗口[2,3]中,橙色②与绿色③连接,但在窗口[1,2]中没有与任何对象连接。
    在这里插入图片描述
    在这里插入图片描述
  • Session Window Join
    在执行会话窗口联接时,具有相同键(当“组合”时满足会话条件)的所有元素以成对组合方式联接,并传递给JoinFunction或FlatJoinFunction。同样,这执行一个内部连接,所以如果有一个会话窗口只包含来自一个流的元素,则不会发出任何输出!
    在这里,我们定义了一个会话窗口连接,其中每个会话被至少1ms的间隔分割。有三个会话,在前两个会话中,来自两个流的连接元素被传递给JoinFunction。在第三个会话中,绿色流中没有元素,所以⑧和⑨没有连接!

在这里插入图片描述
在这里插入图片描述

关于Tumbling Window Join案例演示:

  • 使用两个指定Source模拟数据,一个Source是订单明细,一个Source是商品数据。我们通过window join,将数据关联到一起。
import com.alibaba.fastjson.JSON;
import lombok.Data;
import org.apache.flink.api.common.RuntimeExecutionMode;
import org.apache.flink.api.common.eventtime.*;
import org.apache.flink.api.common.functions.JoinFunction;
import org.apache.flink.configuration.Configuration;
import org.apache.flink.streaming.api.datastream.DataStream;
import org.apache.flink.streaming.api.datastream.DataStreamSource;
import org.apache.flink.streaming.api.datastream.SingleOutputStreamOperator;
import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;
import org.apache.flink.streaming.api.functions.source.RichSourceFunction;
import org.apache.flink.streaming.api.windowing.assigners.TumblingEventTimeWindows;
import org.apache.flink.streaming.api.windowing.time.Time;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

/**
 * @author liu a fu
 * @version 1.0
 * @date 2021/8/5 0005
 * @DESC 演示Flink双流Join-windowJoin
 */
public class JoinDemo01_WindowJoin {
   
    public static void main(String[] args) throws Exception {
   
        //TODO:1-env
        StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
        env.setRuntimeMode(RuntimeExecutionMode.AUTOMATIC);

        //TODO:Sorce
        //商品的数据流
        DataStreamSource<Goods> goodsDS = env.addSource(new GoodsSource());
        //订单的数据流
        DataStreamSource<OrderItem> orderItemDS = env.addSource(new OrderItemSource());
//给数据添加水印(这里简单一点直接使用系统时间作为事件时间)
        /*
         SingleOutputStreamOperator<Order> orderDSWithWatermark = orderDS.assignTimestampsAndWatermarks(
                WatermarkStrategy.<Order>forBoundedOutOfOrderness(Duration.ofSeconds(3))//指定maxOutOfOrderness最大无序度/最大允许的延迟时间/乱序时间
                        .withTimestampAssigner((order, timestamp) -> order.getEventTime())//指定事件时间列
        );
         */
        SingleOutputStreamOperator<Goods> goodsDSWithWatermark = goodsDS.assignTimestampsAndWatermarks(new GoodsWatermark());
        SingleOutputStreamOperator<OrderItem> orderItemDSWithWatermark = orderItemDS.assignTimestampsAndWatermarks(new OrderItemWatermark());

        //TODO:transformation 重点
        DataStream<FactOrderItem> resultDS = goodsDSWithWatermark.join(orderItemDSWithWatermark)
                .where(Goods::getGoodsId)
                .equalTo(OrderItem::getGoodsId)
                .window(TumblingEventTimeWindows.of(Time.seconds(5)))
                /**
                 * <IN1> The type of the elements in the first input.
                 * <IN2> The type of the elements in the second input.
                 * <OUT> The type of the result elements.
                 */
                .apply(new JoinFunction<Goods, OrderItem, FactOrderItem>() {
   
                    @Override
                    public FactOrderItem join(Goods goods, OrderItem orderItem) throws Exception {
   
                        FactOrderItem result = new FactOrderItem();
                        result.setGoodsId(goods.getGoodsId());   //
                        result.setGoodsName(goods.getGoodsName());
                        result.setCount(new BigDecimal(orderItem.getCount()));
                        result.setTotalMoney(new BigDecimal(orderItem.getCount()).multiply(goods.getGoodsPrice()));

                        return result;
                    }
                });

        //TODO: SINK
        resultDS.print();

        //TODO: 获取流试执行环境
        env.execute();

    }

    //商品类(商品id,商品名称,商品价格)
    @Data
    public static class Goods {
   
        private String goodsId;
        
<think>好的,我现在需要帮助用户理解FlinkSQL中双流Inner Join的触发机制以及Watermark的处理方式。用户可能已经有一些基础,但需要更详细的机制解释,特别是触发条件和水印如何影响Join操作。 首先,我得回忆一下Flink双流Join相关知识。根据引用[2]和[3],Flink双流Join有两种主要类型:基于窗口的Join和基于状态的Join。用户提到的是Inner Join,而FlinkSQL中通常使用窗口Join或者Interval Join,所以可能需要先确定具体是哪种类型。不过用户的问题更偏向于触发机制和Watermark处理,所以可能需要综合这两方面。 接下来,触发机制方面,窗口Join的触发通常是在窗口结束时,比如滚动窗口的结束时间到达时。而Interval Join则可能是在事件时间范围内,当两条流的数据到达并满足时间间隔条件时触发。但用户问的是FlinkSQL中的Inner Join,这可能需要具体到SQL的语法和实现机制。例如,FlinkSQL的JOIN是否隐式使用窗口,或者是否依赖于Watermark推进来触发Join操作。 关于Watermark的处理,根据引用[1],Watermark用于处理乱序事件,允许一定的延迟。在双流Join中,Watermark会决定何时可以安全地触发Join,因为需要确保所有可能参与该时间窗口的数据都已经到达。例如,当两条流的Watermark都超过窗口的结束时间时,可以触发计算,避免遗漏延迟的数据。 但需要注意,FlinkSQL中的双流Inner Join如果是基于Interval Join的话,可能不需要显式的窗口定义,而是根据事件时间间隔来判断数据的匹配。此时,Watermark的作用是确保不会无限期等待数据,当Watermark超过某个时间点后,之前的数据可能被清除,避免状态无限增长。 用户的问题可能涉及到何时触发Join输出结果,这取决于两条流的Watermark进度。例如,只有当两条流的Watermark都达到或超过某个时间戳时,才能确定在这个时间之前的所有数据已经到达,从而触发Join操作。 另外,需要解释Watermark在双流Join中的协调机制。比如,Flink会跟踪每个流的Watermark,并以较小的那个作为整体的Watermark进度。这样,当处理双流Join时,系统会根据这个整体Watermark来决定哪些数据可以安全地进行Join,而不会遗漏还在等待的数据。 还需要考虑乱序数据的处理,allowedLateness参数可能影响Join的行为。但根据引用[1],allowedLateness可能与Watermark一起使用,允许一定时间的延迟数据被处理。但在双流Join中,这个参数如何应用可能需要进一步说明。 最后,可能需要结合FlinkSQL的具体语法和配置参数,比如如何设置Watermark的生成策略,以及如何定义JOIN的条件,特别是时间属性字段。例如,在FlinkSQL中,JOIN操作通常需要指定事件时间字段,并且可能需要使用Watermark来推进时间进展。 总结一下,回答的结构应该包括触发机制和Watermark处理两大部分。触发机制部分需要说明Join触发的条件,比如窗口结束或事件时间间隔满足,而Watermark处理部分需要解释Watermark如何协调双流进度,确保数据完整性和处理延迟数据。同时,需要引用提供的资料中的相关部分,比如窗口状态、Watermark生成和双流Join类型等。</think>### FlinkSQL双流Inner Join触发机制与Watermark处理详解 #### 一、触发机制核心逻辑 FlinkSQL双流Inner Join的触发机制基于**事件时间对齐**与**状态管理**实现,具体分为以下两种模式: 1. **基于窗口的Join触发** - 当使用`WINDOW JOIN`语法时,Join操作会在**窗口闭合时触发计算**。例如: ```sql SELECT * FROM stream1 JOIN stream2 ON stream1.id = stream2.id AND stream1.event_time BETWEEN stream2.event_time - INTERVAL '5' MINUTE AND stream2.event_time + INTERVAL '5' MINUTE ``` - 此时Flink会为两条流维护窗口状态,当两条流的Watermark均超过窗口结束时间时触发Join[^2][^3]。 2. **基于Interval Join的触发** - 对于无显式窗口定义的JoinFlink会通过**时间区间匹配**触发。当两条流中任意一条流的数据到达时,系统会检查另一条流的状态存储中是否存在**时间重叠区间**的匹配记录(例如时间差在$[t-Δ, t+Δ]$范围内),存在则立即输出结果[^3]。 #### 二、Watermark处理机制 Watermark在双流Join中承担**时间进度协调者**角色: 1. **Watermark对齐规则** - Flink会分别跟踪两条流的Watermark(记为$W1$和$W2$),取最小值作为全局进度:$W_{global} = \min(W1, W2)$ - 所有时间戳$t \leq W_{global}$的数据被视为**已完整到达**,此时触发Join计算并清理过期状态[^1] 2. **乱序数据处理** - 通过`WATERMARK FOR column AS column - INTERVAL '5' SECOND`定义允许的延迟 - 示例水印生成代码: ```sql CREATE TABLE orders ( id INT, order_time TIMESTAMP(3), WATERMARK FOR order_time AS order_time - INTERVAL '2' SECOND ) ``` - 该设置允许处理最大延迟2秒的乱序数据,在Watermark超过事件时间后仍保留状态等待匹配[^1] 3. **状态保留策略** - 状态存储时间 = 最大匹配时间差(如Interval Join的Δ) + 允许延迟时间 - 当$W_{global} > t_{event} + Δ + allowedLateness$时,相关状态会被清除 #### 三、典型处理流程 以订单表(orders)与物流表(logistics)的Join为例: 1. 数据到达时写入各自状态存储 2. 生成Watermark推进全局时间轴 3. 当$W_{global} \geq t_{order} + Δ$时: - 检查物流表中$t_{logistics} \in [t_{order}-Δ, t_{order}+Δ]$的记录 - 输出匹配结果并清理过时状态 $$状态存储量 = \sum_{i=1}^{n} (Δ_i + allowedLateness_i) \times 数据到达速率_i$$ #### 四、配置优化建议 1. 合理设置`allowedLateness`避免状态膨胀 2. 为JOIN条件字段建立索引(如HBase状态后端) 3. 监控`numLateRecordsDropped`指标调整水印策略
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值