【双流(1)|原理】flink 双流join原理(1)Interval Join:state过大? 回撤现象出现时,sink如何处理,还有如何优化回撤?数据出现shuffle时join是如何处理的?

先思考几个问题:

  • 双流join的基本原理是什么?
  • 双流join的分类有哪些,具体的实现是什么?
  • 双流join产生的问题?回撤的情况以及优化的可能性?
  • 多流join数据倾斜与性能优化思路?
  • 多流join的可能性?

一. 流的join和表的join的区别在哪里

  • 左右两边数据是否无穷
  • join的结果是否会更新,流join会有回撤的现象
  • 流join语义实现的复杂性:双流join左右两边的流速不一致,所以要保存两边的数据到内存中,以保证join语义。

 

二. 双流Join分类

Join大体分类只有两种:Window Join和Interval Join。

Window Join

又分为:

  • Tumbling Window Join
  • Sliding Window Join
  • Session window Join
     

Windows类型的join都是利用window的机制,先将数据缓存在Window State中,当窗口触发计算时,执行join操作;

 
interval join

利用state存储数据再处理,且state中的数据有失效机制,依靠数据触发数据清理;
所以实际开发注意状态的过期时间,免得关联不到数据。

 

三. interval join 的基本逻辑

看一个例子:
我们知道订单数据(一个订单的概况,没有包含哪些商品明细)和订单明细(哪个商品,这个商品属于哪个订单)数据是一对多(包含)的关系,因为一条订单可能包含多个商品。对于双流(inner)join可以这样考虑:

  • 当数据是订单数据到来时,数据的到来均保留到LState中,无论是否关联到明细数据,均留作后续join使用。
  • 当数据是明细数据到来时,只要关联到订单数据,数据就可以清除了,否则存储到RState等待join。
  • 当同一时段的(左右表)数据都消费且join完时,清空存储状态。

对于flink来说:
不论是INNER JOIN还是OUTER JOIN 都需要对左右两边的流的数据进行保存,JOIN算子会开辟左右两个State进行数据存储,左右两边的数据到来时候,进行如下操作:

  1. LeftEvent到来存储到LState,RightEvent到来的时候存储到RState;
  2. LeftEvent会去RightState进行JOIN,并发出所有JOIN之后的Event到下游;
  3. RightEvent会去LeftState进行JOIN,并发出所有JOIN之后的Event到下游。
    在这里插入图片描述

join的结果是数据的笛卡尔积后输出的结果;ing

 

思考一下:

对于订单的例子,使用flink join之后的相同的订单数据会更新多次,这里涉及到了回撤和幂等操作,那带有相同(join的)id的数据到sink要怎么更新?等会儿讨论。

 

1. inner join的逻辑

inner join的一个例子,如下图:

在这里插入图片描述

  • 左右两个流的流入的数据都会一直存储到state里面;
  • 数据是按照如图标号的顺序流入,看到右边的流先流入了三个事件,但左边没有事件,先存入RState;
  • 当左边4事件流入时,先存储到LState,然后和RState的三个事件join(如图4事件的join result),三行结果流到下游;
  • 当右边5事件流入时,先存储到RState,然后和LState进行join(如图5事件的join result),然后输出到下游;

inner join的语义

只有join上数据才会流出到下游
因为保存了两个流所有的事件到state中,所以不管数据是如何乱序,都能保证和传统数据库一样的join语义

 

2. left outer join 的逻辑与回撤

首先:和inner join 相比 left join 不管右流是否进行了join,左流的数据都需要流入下游节点。

 
看一个场景:
查询产品库存和订单数量,库存变化事件流和订单事件流进行LEFT JOIN,JION条件是产品ID,具体如下图:

  • 其中数字代表数据的流入顺序:看到左流的数据首先到达了计算任务,不管右流是否有数据,直接输出“+P001,10,null”,其中左流的数据一致维护在state中;
  • (左流)事件2接着流入,然后直接输出,(右流)事件3流入,看到LState中有能join上的数据,因为是第一个join(仅有第一条数据会撤回?ing),需要撤回(做一个标记其实是?)左边下发的“+P001,10,null”数据,之后下发join后的数据到下游;
  • 后续到来的4,5,6,8等待后续P001的事件是不会产生撤回记录的。
    在这里插入图片描述

注意:在Apache Flink系统内部事件类型分为正向事件标记为“+”和撤回事件标记为“-”*

 

四. Interval Join问题讨论

1. state内存过大的问题 ing

对于left 和 inner join有一个问题我们可以思考下:
因为保存了所有事件的state,所以当数据量很大时,是否容易出现OOM,还是有什么机制触发内存的清理。

 
 

2. 数据Shuffle怎么保证join实现

分布式流计算所有数据会进行Shuffle,怎么才能保障左右两边流的要JOIN的数据会在相同的节点进行处理呢?
在双流JOIN的场景,我们会利用JOIN中ON的联接key进行partition,确保两个流相同的联接key会在同一个节点处理,这个在flink的源码中有说明。

在这里插入图片描述

 
 

3. 回撤的优化的一些想法

产生回撤信息最根本的一个原因是不断地向下游多次发送更新结果,因此,为了减少更新的频率并降低并发,可以把更新结果累计一部分之后再发送出去。如上图所示:

第一个场景是一个嵌套 AGG 的场景(例如两次 Count操作),在第一层 Group By 尝试将更新结果发送到下游时候会先做一个 Cache,从而减少向下游发送数据频率。当达到了 Cache 的触发条件时,再把更新结果发送到下游。
第二个场景是 Outer Join,前面提到,Outer Join 产生回撤消息是因为左右两边数据的速率不匹配。以 Left Outer Join 为例,可以把左流的数据进行 Cache。左流数据到达时会去右流的状态里面查找,如果能找到可以与之 Join的数据则不作缓存(redis等?);如果找不到相应数据,则对这条 Key 的数据先做缓存,当到达某些触发条件时,再去右流状态中查找一次,如果仍然找不到相应数据,再去向下游发送一条包含 Null 值的 Join 数据,之后右流相应数据到达就会将 Cache 中该 Key 对应的缓存清空,并向下游发送一条回撤消息。

 
 

4. sink节点对于回撤现象的处理

对于 Sink 节点,目前 Flink 中有三种 sink 类型,AppendStreamTableSink、RetractStreamTableSink 和 UpsertStreamTableSink。

  • AppendStreamTableSink:接收的上游数据是一条 Retract 信息的话会直接报错的,因为它只能描述 Append-Only 语义;
  • RetractStreamTableSink:如果上游算子发送一个 Retract 信息过来,它会对消息做 Delete操作,如果上游算子发送的是正常的更新信息,它会对消息做 Insert 操作;
  • UpsertStreamTableSink:可以理解为对于RetractStreamTableSink 做了一些性能的优化。如果 Sink 数据源支持幂等操作,或者支持按照某 key 做 Update 操作,UpsertStreamTableSink 会在 SQL 翻译的时候把上游 Upsert Key 传到 Table Sink 里面,然后基于该 Key 去做 Update 操作。这样输出的多条相同key的数据就可以更新为一条。

 
 
参考:
Apache Flink 漫谈系列(09) - JOIN 算子

  • 3
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

roman_日积跬步-终至千里

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值