在Flink中用(X,Y)键控的快速流充实(X)键控的缓慢变化流

时间:2019-11-27 15:16:11

标签: apache-flink flink-streaming

我需要以(userId)为键的缓慢变化的streamA来充实以(userId,startTripTimestamp)为键的快速变化的streamB

我将Flink 1.8与DataStream API一起使用。我考虑两种方法:

  1. 广播streamB,并通过userId和最新时间​​戳加入流。它等同于TableAPI中的DynamicTable吗?我可以看到此解决方案的一些缺点:streamB需要适合每个工作程序节点的RAM,因为整个streamB需要存储在每个工作程序的RAM中,所以它提高了RAM的利用率。

  2. streamA的状态概括为仅以(userId)为键的流,我们将其命名为streamC,以与streamB具有公用密钥。然后,我可以将streamCstreamB结合起来,按处理时间排序,并处理状态下的两种类型的事件。处理一般的流(过程函数中的更多代码)更为复杂,但不会消耗太多的RAM来在所有节点上拥有所有streamB。他们在该解决方案中还有其他弊端吗?

我还看到了这样的建议https://cwiki.apache.org/confluence/display/FLINK/FLIP-17+Side+Inputs+for+DataStream+API

  

通常,大多数此类活动都遵循加入主流的模式   一个或多个缓慢变化的输入或   静态数据:

     

[...]

     

加入流与缓慢发展的数据流:这非常类似于   上述情况,但我们用于充实的侧输入是   随着时间的推移而发展。这可以通过等待一些初始数据来完成   在处理主输入和连续输入之前可用   将新数据吸收到内部输入结构中   到达。

不幸的是,要达到此功能https://issues.apache.org/jira/browse/FLINK-6131似乎还需要很长的时间,并且没有其他描述。因此,我想问一下针对所描述用例的当前推荐方法。

我见过Combining low-latency streams with multiple meta-data streams in Flink (enrichment),但是它没有指定该流的键,而且在Flink 1.4时已得到回答,所以我希望推荐的解决方案可能已经改变。

2 个答案:

答案 0 :(得分:3)

以Gaurav Kumar已回答的内容为基础。

主要问题是,您是否需要完全匹配streamAstreamB中的记录,还是尽力而为?例如,对您来说,这是一个问题吗,由于竞争状况,streamA中的某些(很多?)记录可以在streamB中的某些更新到达之前(例如在启动期间)进行处理?

我建议从Table API is solving this issue的方式中汲取灵感。可能是临时表联接是您的正确选择,这会让您选择:处理时间还是事件时间?

Gaurav Kumar的提议中的两个都是实现of processing time Temporal Table joins,它们假设记录可以非常松散地连接,并且不必正确地计时。

如果必须正确定时streamAstreamB中的记录,那么就必须用一种或另一种方法来缓冲来自两个流的某些记录。具体方法有多种,具体取决于您要实现的semantic。决定了这一点之后,实际的实现并不困难,您可以从Table API联接运算符(org.apache.flink.table.runtime.join模块中的flink-table-planner包)中汲取灵感。

侧输入(您引用的)和/或输入选择仅是用于控制不必要的缓冲记录的数量的工具。您可以在没有它们的情况下实现有效的Flink作业,但是如果一个流明显超过另一个流(就事件时间而言,对于处理时间而言,它不是问题),则很难控制内存消耗。

答案 1 :(得分:1)

答案取决于您用来充实streamB的{​​{1}}状态的大小

  • 如果您广播streamA状态,那么您会将所有streamB的用户ID放置到每个任务管理器中。任务管理器上的每个任务将仅具有来自streamA的这些用户ID的子集。因此,streamB中的某些userId数据将永远不会使用,并且会浪费。因此,如果您认为streamB状态的大小不足以真正影响您的工作,并且不占用大量内存来为状态管理留下更少的内存,则可以保留整个streamB状态。这是您的#1。
  • 如果您的streamB状态确实很大,并且会占用任务管理器的大量内存,则应考虑方法2。通过相同的ID标识两个流,以确保具有相同userID的元素完成相同的任务,然后可以使用托管状态来维护每个密钥streamB状态,并使用此托管状态来丰富streamA元素。