Apache spark Spark流:无状态重叠窗口与保持状态

Apache spark Spark流:无状态重叠窗口与保持状态,apache-spark,spark-streaming,Apache Spark,Spark Streaming,在使用Spark Streaming处理连续、有限事件会话流时,选择无状态滑动窗口操作(例如ReduceByAndWindow)与选择保持状态(例如通过updateStateByKey或新的mapStateByKey)有哪些注意事项 例如,考虑下面的场景: 一种可穿戴设备可以跟踪由用户进行的体育锻炼 佩戴者。当运动开始时,设备会自动检测, 发出信息,;在练习时发出其他消息 正在进行(如心率);最后,当 练习完成了 期望的结果是每个练习会话的聚合记录流。i、 e.同一会话的所有事件应聚合在一起(例

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

例如,考虑下面的场景:

一种可穿戴设备可以跟踪由用户进行的体育锻炼 佩戴者。当运动开始时,设备会自动检测, 发出信息,;在练习时发出其他消息 正在进行(如心率);最后,当 练习完成了

期望的结果是每个练习会话的聚合记录流。i、 e.同一会话的所有事件应聚合在一起(例如,使每个会话可以保存在单个DB行中)。请注意,每个会话的长度有限,但来自多个设备的整个流是连续的。为方便起见,假设设备为每个练习会话生成GUID

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

  • 使用非重叠窗口,并保持状态。每个GUID保存一个状态,所有事件都与之匹配。当新事件到达时,状态将更新(例如,使用mapWithState),如果事件为“练习会话结束”,则将发出基于状态的聚合记录,并移除密钥

  • 使用重叠的滑动窗口,只保留第一个会话。假设滑动窗口的长度为2,间隔为1(见下图)。同时假设窗口长度为2 X(最大可能运动时间)。在每个窗口上,事件按GUID进行聚合,例如使用reduceByKeyAndWindow。然后,转储在窗口后半部分启动的所有会话,并发出其余会话。这允许每个事件只使用一次,并确保属于同一会话的所有事件将聚合在一起

  • 进近图#2:

    利与弊我明白了:

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

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

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


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

    通常没有正确的方法,每个方法都有权衡。因此,我将添加额外的方法来混合,并将概述我对其利弊的看法。所以你可以决定哪一个更适合你

    外部状态方法(方法3) 您可以在外部存储器中累积事件的状态。卡桑德拉经常被用来做这件事。您可以分别处理最终事件和正在进行的事件,例如,如下所示:

    val流=。。。
    val ongoingeventstream=stream.filter(!isFinalEvent)
    val finalEventsStream=stream.filter(isFinalEvent)
    OnGoingEventStream.foreachRDD{/*casssandra中的累积状态*/}
    finalEventsStream.foreachRDD{/*在casssandra中完成状态,如果需要,移动到最终目标*/}
    
    trackStateByKey进近(进近#1.1) 对于您来说,这可能是一个潜在的最佳解决方案,因为它消除了updateStateByKey的缺点,但考虑到它只是作为Spark 1.6发行版的一部分发布的,它也可能有风险(因为出于某些原因,它不是很受欢迎)。如果您想了解更多信息,可以使用作为起点

    赞成/反对 进近#1(updateStateByKey) 赞成的意见
    • 易于理解或解释(对团队其他成员、新员工等)(主观)
    • 存储:更好地利用内存只存储最新的运动状态
    • 存储:将只保留正在进行的练习,并在完成后立即丢弃
    • 延迟仅受每个微批处理的性能限制
    欺骗
    • 存储:如果密钥数(并发练习)很大,则可能无法放入集群的内存中
    • 处理:它将为状态映射中的每个键运行updateState函数,因此,如果并发练习的数量很大,性能将受到影响
    进近#2(窗口) 虽然使用windows可以实现您所需的功能,但在您的场景中,它看起来明显不那么自然

    赞成的意见
    • 处理在某些情况下(取决于数据)可能比updateStateByKey更有效,因为updateStateByKey倾向于在每个键上运行更新,即使没有实际更新
    欺骗
    • “最大可能的运动时间”——这听起来像是一个巨大的风险——根据人类行为,它可能是相当任意的时间。有些人可能会忘记“完成练习”。这也取决于锻炼的种类,但可能从几秒到几小时不等,当你希望快速锻炼的潜伏期较低时,就必须将潜伏期保持在可能存在的最长锻炼的潜伏期
    • 感觉很难向他人解释它将如何工作(主观)
    • 存储:必须将所有数据保存在窗口框架内,而不仅仅是最新的数据。只有当窗口从这个时间段滑开时,而不是当练习实际完成时,才会释放内存。虽然如果你只保留最后两个时间段可能不会有太大的区别,但如果你试图通过更频繁地滑动窗口来获得更大的灵活性,那么这种差异就会增加
    进近#3(外部状态) 赞成的意见
    Only sessions starting in the areas marked with \\\ will be emitted. 
    -----------
    |window 1 |
    |\\\\|    |
    -----------
         ----------
         |window 2 |
         |\\\\|    |  
         -----------
              ----------
              |window 3 |
              |\\\\|    |
              -----------
    
    ongoingEventsStream.foreachRDD { /*accumulate state in casssandra*/ }