我们可以在spark.map函数中使用外部地图对象吗?

时间:2015-10-12 18:21:17

标签: scala mapreduce apache-spark scala-collections

我是scala& amp;的新手函数式编程。我有以下火花代码片段:

case class SPR(symbol:String, splitOrg:Double, splitAdj:Double, timeStamp: String, unx_tt: Int)

var oldFct = 15.0
val splitMap = collection.mutable.Map[String, Double]()

val tmp = splitsData.map{ row=>
    var newFct = 1.0;
    var sym = row(0).toString;
    oldFct = splitMap.getOrElse(sym, 1.0)
    newFct = row(12).toString.toDouble * oldFct
    splitMap += (sym->newFct)
    SPR(row(0).toString, row(12).toString.toDouble, newFct, row(10).toString, row(13).toString.toInt)
}.collect()

println("MAP ===========" + splitMap.size)

根据我的观察,我可以在块内部使用原始数据类型,但是在Map对象的情况下,我总是将大小设置为0.因此似乎没有键,值对被添加。

提前致谢。

1 个答案:

答案 0 :(得分:2)

在Spark文档中阅读Understanding closures。最相关的部分(只需将counter替换为splitMap):

  

修改其范围之外的变量的RDD操作可能经常引起混淆......

     

主要挑战是上述代码的行为未定义。在具有单个JVM的本地模式下,上述代码将对RDD中的值求和并将其存储在计数器中。这是因为RDD和变量计数器都在驱动程序节点上的相同内存空间中。

     

但是,在群集模式下,发生的事情会更复杂,并且上述内容可能无法按预期工作。为了执行作业,Spark将RDD操作的处理分解为任务 - 每个任务都由执行者操作。在执行之前,Spark计算闭包。闭包是那些变量和方法,它们必须是可见的,以便执行程序在RDD上执行其计算(在本例中为foreach())。该闭包被序列化并发送给每个执行者。在本地模式下,只有一个执行程序,所以一切都共享相同的闭包。但是,在其他模式中,情况并非如此,并且在单独的工作节点上运行的执行程序每个都有自己的闭包副本。

     

这里发生的事情是发送给每个执行程序的闭包内的变量现在是副本,因此,当在foreach函数中引用计数器时,它不再是驱动程序节点上的计数器。驱动程序节点的内存中仍然有一个计数器,但执行程序不再可见!执行程序只能看到序列化闭包中的副本。因此,计数器的最终值仍然为零,因为计数器上的所有操作都引用了序列化闭包中的值。

     

为确保在这些场景中定义良好的行为,应使用累加器。 Spark中的累加器专门用于提供一种机制,用于在跨集群中的工作节点分割执行时安全地更新变量。本指南的累加器部分更详细地讨论了这些。

     

通常,闭包 - 类似循环或本地定义的方法的构造不应该用于改变某些全局状态。 Spark没有定义或保证从闭包外部引用的对象的突变行为。执行此操作的某些代码可能在本地模式下工作,但这只是偶然的,并且此类代码在分布式模式下不会按预期运行。如果需要某些全局聚合,请使用累加器。