为什么火花工人的内存使用会随着时间而增加?

时间:2017-03-07 06:24:07

标签: scala apache-spark spark-streaming

我运行了一个Spark Streaming应用程序,它使用 mapWithState 函数来跟踪RDD的状态。 应用程序运行良好几分钟但随后崩溃

org.apache.spark.shuffle.MetadataFetchFailedException: Missing an output location for shuffle 373

我观察到,即使我已经为mapWithStateRDD设置了超时,Spark应用程序的内存使用也会随着时间线性增加。请参阅下面的代码段和内存使用情况 -

val completedSess = sessionLines
                    .mapWithState(StateSpec.function(trackStateFunction _)
                    .numPartitions(80)
                    .timeout(Minutes(5)))

enter image description here

如果每个RDD都有明确的超时,为什么内存会随时间线性增加?

我试过增加内存但没关系。我错过了什么?

编辑 - 参考代码

def trackStateFunction(batchTime:Time,key:String,value:Option [String],state:State [(Boolean,List [String],Long)]):Option [(Boolean,List [String])] = {

  def updateSessions(newLine: String): Option[(Boolean, List[String])] = {
    val currentTime = System.currentTimeMillis() / 1000

    if (state.exists()) {
      val newLines = state.get()._2 :+ newLine

      //check if end of Session reached.
      // if yes, remove the state and return. Else update the state
      if (isEndOfSessionReached(value.getOrElse(""), state.get()._4)) {
        state.remove()
        Some(true, newLines)
      }
      else {
        val newState = (false, newLines, currentTime)
        state.update(newState)
        Some(state.get()._1, state.get()._2)
      }
    }
    else  {
      val newState = (false, List(value.get), currentTime)
      state.update(newState)
      Some(state.get()._1, state.get()._2)
    }
  }

  value match {
    case Some(newLine) => updateSessions(newLine)
    case _ if state.isTimingOut() => Some(true, state.get()._2)
    case _ => {
      println("Not matched to any expression")
      None
    }
  }
}

2 个答案:

答案 0 :(得分:1)

根据mapwithstate的信息: 州规范 初始状态为RDD - 您可以从某个商店加载初始状态,然后以该状态启动流式传输作业。

分区数 - 键值状态dstream由键分区。如果您之前对状态的大小有一个很好的估计,则可以提供相应的分区数量。

分区程序 - 您还可以提供自定义分区程序。默认分区程序是散列分区程序。如果您对密钥空间有很好的理解,那么您可以提供一个自定义分区程序,它可以进行有效的更新,而不是默认的散列分区程序。

超时 - 这将确保在特定时间段内未更新其值的键将从状态中删除。这可以帮助用旧密钥清理状态。

因此,超时只与使用未更新的键一段时间后进行清理有关。内存将运行完全并最终阻塞,因为执行程序没有分配足够的内存。这给出了MetaDataFetchFailed异常。随着记忆的增加,我希望你的意思是执行者。即使这样,增加执行程序的内存也可能不起作用,因为流仍在继续。使用MapWithState,会话行将包含与输入dstream相同的记录数。所以解决这个问题就是让你的dstream更小。在流式上下文中,您可以设置批次间隔,这很可能会解决此问题

val ssc = new StreamingContext(sc,Seconds(batchIntervalSeconds))

请记住偶尔制作快照和检查点。快照将允许您使用现在较早丢失的流中的信息进行其他计算。希望这有助于获取更多信息,请参阅:https://docs.cloud.databricks.com/docs/spark/1.6/examples/Streaming%20mapWithState.htmlhttp://asyncified.io/2016/07/31/exploring-stateful-streaming-with-apache-spark/

答案 1 :(得分:0)

mapWithState还将mappedValues存储在RAM中(参见MapWithStateRDD),默认情况下mapWithState存储在RAM中最多20个MapWithStateRDD。

简要说明RAM使用率与批处理间隔成正比,

您可以尝试减少批处理间隔以减少RAM使用量。