在使用Spark Streaming处理连续的有限事件 session 流时,选择无状态滑动窗口操作(例如reduceByKeyAndWindow)与选择保持状态(例如通过updateStateByKey或新的mapStateByKey)会有什么考虑?
例如,考虑以下情形:
A wearable device tracks physical exercises performed by the wearer. The device automatically detects when an exercise starts, and emits a message; emits additional messages while the exercise is undergoing (e.g. heart rate); and finally, emits a message when the exercise is done.
理想的结果是每个练习的汇总记录流。也就是说,同一 session 的所有事件都应该汇总在一起(例如,以便每个 session 都可以保存在单个数据库行中)。请注意,每个 session 的长度是有限的,但是来自多个设备的整个流是连续的。为了方便起见,让我们假设设备为每次锻炼生成一个GUID。
我可以看到两种通过Spark Streaming处理此用例的方法:
方法2的示意图:
Only sessions starting in the areas marked with \\\ will be emitted. ----------- |window 1 | |\\\\| | ----------- ---------- |window 2 | |\\\\| | ----------- ---------- |window 3 | |\\\\| | -----------
我看到的利弊:
方法#1的计算开销较小,但是需要保存和管理状态(例如,如果并发 session 数增加,则状态可能会大于内存)。但是,如果最大并发 session 数是有界的,这可能不是问题。
方法2的成本是两倍(每个事件被处理两次),并且具有更高的延迟(最大运动时间是2倍),但是由于没有保留任何状态,因此更简单易管理。
处理该用例的最佳方法是什么-这些方法中的任何一种是“正确的”方法,还是有更好的方法?
应该考虑哪些其他优点/缺点?
最佳答案
通常没有正确的方法,每个方法都有权衡。因此,我将添加其他方法,并概述我对他们的利弊的看法。因此,您可以决定哪一个更适合您。
外部状态方法(方法3)
您可以在外部存储中累积事件的状态。 Cassandra 经常被用于此。您可以分别处理最终事件和正在进行的事件,如下所示:
val stream = ...
val ongoingEventsStream = stream.filter(!isFinalEvent)
val finalEventsStream = stream.filter(isFinalEvent)
ongoingEventsStream.foreachRDD { /*accumulate state in casssandra*/ }
finalEventsStream.foreachRDD { /*finalize state in casssandra, move to final destination if needed*/ }
trackStateByKey方法(方法#1.1)
这可能是您潜在的最佳解决方案,因为它消除了updateStateByKey的缺点,但考虑到它只是作为Spark 1.6版本的一部分发布的,因此它也可能存在风险(由于某种原因,它并不十分广告)。如果您想了解更多信息,可以使用link作为起点
优点缺点
方法1(updateStateByKey)
优点
缺点
方法2(窗口)
尽管可以通过Windows实现所需的功能,但在您的情况下看起来自然度大大降低。
优点
缺点
方法3(外部状态)
优点
缺点
建议的方法
我会尝试以下方法:
关于apache-spark - Spark Streaming:无状态重叠窗口与保持状态,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34630251/