Spark Streaming mapWithState似乎定期重建完整状态

时间:2016-03-16 17:02:46

标签: scala apache-spark spark-streaming

我正在开发一个Scala(2.11)/ Spark(1.6.1)流式项目,并使用mapWithState()来跟踪以前批次中看到的数据。

状态分布在多个节点上的20个分区中,使用StateSpec.function(trackStateFunc _).numPartitions(20)创建。在这种状态下,我们只有几个键(~100)映射到Sets,有大约160,000个条目,这些条目在整个应用程序中都会增长。整个状态最多为3GB,可由群集中的每个节点处理。在每个批次中,一些数据被添加到一个状态,但直到过程结束时才被删除,即大约15分钟。

在遵循应用程序UI时,与其他批次相比,每10个批次的处理时间非常长。见图:

The spikes show the higher processing time.

黄色字段代表高处理时间。

enter image description here

更详细的“作业”视图显示,在这些批次中,在某个点发生,恰好在“跳过”所有20个分区时。或者这就是用户界面所说的内容。

enter image description here

我对skipped的理解是每个状态分区都是一个未执行的可能任务,因为它不需要重新计算。但是,我不明白为什么每个工作中skips的数量会有所不同,以及为什么上一个工作需要这么多处理。无论状态大小如何,都会出现更高的处理时间,只会影响持续时间。

这是mapWithState()功能中的错误还是这种预期的行为?底层数据结构是否需要某种重新排列,状态中的Set是否需要复制数据?或者它更可能成为我申请中的缺陷?

2 个答案:

答案 0 :(得分:10)

  

这是mapWithState()功能中的错误还是这个错误   行为?

这是预期的行为。您看到的高峰是因为您的数据在给定批次结束时被检查点。如果您注意到较长批次的时间,您会发现它每100秒持续发生一次。这是因为检查点时间是常数,并且是按照batchDuration计算的,这是您与数据源进行通信以读取批次乘以某个常量的频率,除非您明确设置{{1}间隔。

以下是DStream.checkpoint的相关代码:

MapWithStateDStream

override def initialize(time: Time): Unit = { if (checkpointDuration == null) { checkpointDuration = slideDuration * DEFAULT_CHECKPOINT_DURATION_MULTIPLIER } super.initialize(time) } 的位置:

DEFAULT_CHECKPOINT_DURATION_MULTIPLIER

与您所看到的行为完全一致,因为您的批量持续时间是每10秒=> 10 * 10 = 100秒。

这是正常的,这是使用Spark保持状态的成本。您的优化可能是考虑如何最小化您必须保留在内存中的状态的大小,以便尽可能快地进行此序列化。另外,确保数据遍布足够的执行程序,以便状态在所有节点之间均匀分布。此外,我希望您已打开Kryo Serialization而不是默认的Java序列化,这可以为您带来有意义的性能提升。

答案 1 :(得分:1)

除了接受的答案,指出与检查点相关的序列化价格之外,还有另一个鲜为人知的问题可能导致spikey行为:驱逐已删除的状态。

具体而言,“删除”或“超时”状态不会立即从地图中删除,而是标记为删除,并且仅在序列化过程中实际删除[在Spark 1.6.1中,请参阅writeObjectInternal()]

这有两个性能影响,每10批次只发生一次:

  1. 遍历和删除过程有其价格
  2. 如果您处理超时/删除事件流,例如将其保留在外部存储中,所有10个批次的相关成本仅在此时支付(并且不是每个RDD上可能预期的那样)