【问题标题】:Spark Streaming: stateless overlapping windows vs. keeping stateSpark Streaming:无状态重叠窗口与保持状态
【发布时间】:2016-04-10 08:58:08
【问题描述】:

在使用 Spark Streaming 处理连续的有限事件会话流时,选择无状态滑动窗口操作(例如 reduceByKeyAndWindow)与选择保持状态(例如通过 updateStateByKey 或新的 mapStateByKey)有哪些注意事项?

例如,考虑以下场景:

可穿戴设备可跟踪由以下人员进行的体育锻炼 佩戴者。该设备会自动检测锻炼何时开始, 并发出消息;锻炼时发出额外的信息 正在经历(例如心率);最后,当 练习完成了。

所需的结果是每个锻炼会话的汇总记录流。即同一会话的所有事件应聚合在一起(例如,以便每个会话可以保存在单个数据库行中)。请注意,每个会话的长度都是有限的,但来自多个设备的整个流是连续的。为方便起见,我们假设设备为每个锻炼会话生成一个 GUID。

我可以看到两种使用 Spark Streaming 处理此用例的方法:

  1. 使用非重叠窗口并保持状态。每个 GUID 保存一个状态,所有事件都与之匹配。当一个新的事件到来时,状态被更新(例如使用mapWithState),如果事件是“运动会话结束”,一个基于状态的聚合记录将被发出,并且key被移除。

  2. 使用重叠的滑动窗口,只保留第一个会话。假设一个长度为 2 且间隔为 1 的滑动窗口(见下图)。还假设窗口长度为 2 X(最大可能锻炼时间)。在每个窗口上,事件按 GUID 聚合,例如使用 reduceByKeyAndWindow。然后,在窗口后半部分开始的所有会话都被转储,剩余的会话被发射。这使得每个事件只使用一次,并确保属于同一会话的所有事件都将聚合在一起。

方法 #2 的示意图:

Only sessions starting in the areas marked with \\\ will be emitted. 
-----------
|window 1 |
|\\\\|    |
-----------
     ----------
     |window 2 |
     |\\\\|    |  
     -----------
          ----------
          |window 3 |
          |\\\\|    |
          -----------

我看到的优点和缺点:

方法 #1 的计算成本较低,但需要保存和管理状态(例如,如果并发会话的数量增加,状态可能会大于内存)。但是,如果最大并发会话数是有界的,这可能不是问题。

方法 #2 的成本是前者的两倍(每个事件处理两次),并且延迟更高(最大锻炼时间的 2 倍),但更简单且易于管理,因为不保留任何状态。

处理此用例的最佳方法是什么 - 这些方法中的任何一种是“正确”的方法,还是有更好的方法?

还应考虑哪些其他优点/缺点?

【问题讨论】:

    标签: apache-spark spark-streaming


    【解决方案1】:

    通常没有正确的方法,每种方法都有取舍。因此,我会在组合中添加其他方法,并将概述我对它们的优缺点的看法。所以你可以决定哪一个更适合你。

    外部状态方法(方法 #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)

    优点

    • 易于理解或解释(对团队其他成员、新人等)(主观)
    • 存储:更好地利用内存只存储最新的运动状态
    • 存储:将仅保留正在进行的练习,并在完成后立即丢弃它们
    • 延迟仅受每个微批处理性能的限制

    缺点

    • 存储:如果键(并发练习)的数量很大,它可能不适合您的集群的内存
    • 处理:它将为状态图中的每个键运行 updateState 函数,因此如果并发练习的数量很大 - 性能会受到影响

    方法#2(窗口)

    虽然可以通过 windows 实现您所需要的,但在您的场景中看起来不太自然。

    优点

    • 处理在某些情况下(取决于数据)可能比 updateStateByKey 更有效,因为即使没有实际更新,updateStateByKey 也会对每个键运行更新

    缺点

    • “最大可能的锻炼时间”——这听起来像是一个巨大的风险——它可能是基于人类行为的任意时间。有些人可能会忘记“完成运动”。也取决于锻炼的种类,但范围可能从几秒到几小时不等,当您希望快速锻炼的延迟时间较短,而您必须将延迟保持在可能存在的最长锻炼时的最高水平
    • 感觉很难向其他人解释它的工作原理(主观)
    • 存储:必须将所有数据保存在窗口框架内,而不仅仅是最新的。只有当窗口从这个时间槽滑开时才会释放内存,而不是在运动实际完成时。虽然如果您只保留最后两个时间段可能不会有太大差异 - 如果您尝试通过更频繁地滑动窗口来实现更大的灵活性,它会增加。

    方法#3(外部状态)

    优点

    • 容易解释等(主观)
    • 纯流式处理方法,意味着 spark 负责对每个单独的事件进行操作,而不是尝试存储状态等(主观)
    • 存储:不受集群内存限制以存储状态 - 可以处理大量并发练习
    • 处理:状态仅在有实际更新时才更新(与 updateStateByKey 不同)
    • 延迟类似于 updateStateByKey,仅受处理每个微批处理所需的时间限制

    缺点

    • 架构中的额外组件(除非您已将 Cassandra 用于最终输出)
    • 处理:默认情况下比仅在 spark 中处理慢,因为不在内存中 + 您需要通过网络传输数据
    • 您必须实现一次语义才能将数据输出到 cassandra(针对 foreachRDD 期间工作人员失败的情况)

    建议的方法

    我会尝试以下方法:

    • 在您的数据和集群上测试 updateStateByKey 方法
    • 查看内存消耗和处理是否可以接受,即使有大量并发练习(预计在高峰时段)
    • 以防万一

    【讨论】:

    • 谢谢!方法#3 确实是一个受欢迎的补充。 TrackStateByKey 在性能方面确实会更好,但仍有存储空间。
    • 我在使用 mapWithState 方面拥有丰富的经验。看看这个超级有用的教程:asyncified.io/2016/07/31/…
    【解决方案2】:

    我认为第三种方法的其他缺点之一是没有按时间顺序接收 RDD..考虑在集群上运行它们..

    ongoingEventsStream.foreachRDD { /*accumulate state in casssandra*/ }
    

    还有检查点和驱动节点故障的情况。在那种情况下,你会再次读取整个数据吗?想知道您想如何处理这个问题?

    我想也许 mapwithstate 是一个更好的方法,为什么你要考虑所有这些场景..

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2023-01-20
      • 2022-11-15
      • 1970-01-01
      • 2014-09-11
      • 1970-01-01
      • 1970-01-01
      • 2018-03-17
      • 2021-04-27
      相关资源
      最近更新 更多