我需要用缓慢变化的streamB
键(userId(来丰富我的快速变化streamA
(userId,startTripTimestamp(。
我将 Flink 1.8 与 DataStream API 一起使用。我考虑两种方法:
-
广播
streamB
并按用户 ID 和最新时间戳加入流。它是否等同于TableAPI中的DynamicTable?我可以看到这个解决方案的一些缺点:streamB
需要适应每个工作节点的 RAM,它会增加 RAM 的利用率,因为整个streamB
需要存储在每个工作线程的 RAM 中。 -
将
streamA
的状态推广到仅由 (userId( 键控的流,我们将其命名为streamC
,以便与streamB
具有公共键。然后,我能够将streamC
与streamB
联合起来,按处理时间排序,并在状态中处理两种类型的事件。处理生成流(进程函数中的更多代码(更为复杂,但不会消耗那么多 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已经回答的基础上。
主要问题是您是否需要完全匹配来自streamA
和streamB
的记录,还是尽力匹配?例如,由于争用条件,来自streamA
的某些(很多?(记录可以在streamB
的某些更新到达之前(例如在启动期间(处理,这对您来说是一个问题吗?
我建议从表 API 如何解决这个问题中汲取灵感。可能临时表联接是您的正确选择,这会让您选择:处理时间还是事件时间?
Gaurav Kumar 的两个建议都是处理时间时态表连接的实现,它假设记录可以非常松散地连接,而不必正确计时。
如果来自streamA
和streamB
的记录必须正确计时,则必须以一种或另一种方式缓冲来自两个流的一些记录。有多种方法可以做到这一点,具体取决于您要实现的语义。确定这一点后,实际实现并不困难,您可以从表 API 连接运算符(flink-table-planner
模块中的org.apache.flink.table.runtime.join
包(中汲取灵感。
侧输入(您引用的(和/或输入选择只是用于控制不必要的缓冲记录数量的工具。您可以在没有它们的情况下实现有效的 Flink 作业,但如果一个流显着超过另一个流,则内存消耗可能很难控制(就事件时间而言 - 对于处理时间而言,这不是问题(。
答案取决于需要用于丰富streamA
的streamB
状态的大小
- 如果广播
streamB
状态,则将 streamB 中的所有用户 ID 放入每个任务管理器。任务管理器上的每个任务上都只有来自 streamA 的这些 userId 的子集。因此,来自 streamB 的一些 userId 数据将永远不会被使用,并且会作为浪费。因此,如果您认为streamB
状态的大小不足以真正影响您的工作,并且不需要大量内存来为状态管理留下更少的内存,则可以保留整个streamB
状态。这是你的#1。 - 如果您的
streamB
状态非常大,并且可能会在任务管理器上消耗大量内存,则应考虑方法#2。KeyBy 同一 ID 两个流,以确保具有相同 userID 的元素到达相同的任务,然后您可以使用托管状态来维护每个键的 streamB 状态并使用此托管状态丰富 streamA 元素。