在 Flink 中使用由 (X) 键控的缓慢变化流来丰富由 (X,Y) 键控的快速流



我需要用缓慢变化的streamB键(userId(来丰富我的快速变化streamA(userId,startTripTimestamp(。

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

  1. 广播streamB并按用户 ID 和最新时间戳加入流。它是否等同于TableAPI中的DynamicTable?我可以看到这个解决方案的一些缺点:streamB需要适应每个工作节点的 RAM,它会增加 RAM 的利用率,因为整个streamB需要存储在每个工作线程的 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 并且没有描述其他选择。因此,我想问一下目前针对所描述的用例推荐的方法。

我已经在 Flink 中看到了将低延迟流与多个元数据流相结合(扩充(,但它没有指定该流的键是什么,而且它在 Flink 1.4 时得到了回答,所以我希望推荐的解决方案可能已经改变。

建立在Gaurav Kumar已经回答的基础上。

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

我建议从表 API 如何解决这个问题中汲取灵感。可能临时表联接是您的正确选择,这会让您选择:处理时间还是事件时间?

Gaurav Kumar 的两个建议都是处理时间时态表连接的实现,它假设记录可以非常松散地连接,而不必正确计时。

如果来自streamAstreamB的记录必须正确计时,则必须以一种或另一种方式缓冲来自两个流的一些记录。有多种方法可以做到这一点,具体取决于您要实现的语义。确定这一点后,实际实现并不困难,您可以从表 API 连接运算符(flink-table-planner模块中的org.apache.flink.table.runtime.join包(中汲取灵感。

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

答案取决于需要用于丰富streamAstreamB状态的大小

  • 如果广播streamB状态,则将 streamB 中的所有用户 ID 放入每个任务管理器。任务管理器上的每个任务上都只有来自 streamA 的这些 userId 的子集。因此,来自 streamB 的一些 userId 数据将永远不会被使用,并且会作为浪费。因此,如果您认为streamB状态的大小不足以真正影响您的工作,并且不需要大量内存来为状态管理留下更少的内存,则可以保留整个streamB状态。这是你的#1。
  • 如果您的streamB状态非常大,并且可能会在任务管理器上消耗大量内存,则应考虑方法#2。KeyBy 同一 ID 两个流,以确保具有相同 userID 的元素到达相同的任务,然后您可以使用托管状态来维护每个键的 streamB 状态并使用此托管状态丰富 streamA 元素。

相关内容

  • 没有找到相关文章

最新更新