Spark mapPartitions vs transient lazy val

时间:2016-11-23 20:42:53

标签: dictionary apache-spark partition transient

我想知道使用spark mapPartitions功能与瞬态懒惰val有什么不同。
由于每个分区基本上都在不同的节点上运行,因此将在每个节点上创建一个瞬态延迟val实例(假设它在一个对象中)。

例如:

class NotSerializable(v: Int) {
  def foo(a: Int) = ???
}

object OnePerPartition {
  @transient lazy val obj: NotSerializable = new NotSerializable(10)
}

object Test extends App{
    val conf = new SparkConf().setMaster("local[2]").setAppName("test")
    val sc = new SparkContext(conf)

    val rdd: RDD[Int] = sc.parallelize(1 to 100000)

    rdd.map(OnePerPartition.obj.foo)

    // ---------- VS ----------

    rdd.mapPartitions(itr => {
      val obj = new NotSerializable(10)
      itr.map(obj.foo)
    })
}

有人可能会问为什么你甚至想要它...
我想创建一个通用的容器概念,用于在任何通用集合实现上运行我的逻辑(RDDListscalding pipe等。)
所有这些都有" map"的概念,但mapPartition对于spark是唯一的。

1 个答案:

答案 0 :(得分:3)

首先,您不需要transient lazy。使用object包装器就足以使其工作,您实际上可以将其写为:

object OnePerExecutor {
  val obj: NotSerializable = new NotSerializable(10)
}

对象包装器与NotSerializable内的mapPartitions初始化之间存在根本区别。这样:

rdd.mapPartitions(iter => {
  val ns = NotSerializable(1)
  ???
})

为每个分区创建一个NotSerializable个实例。

另一方面,

对象包装器为每个执行程序JVM创建一个NotSerializable实例。结果这个例子:

  • 可用于处理多个分区。
  • 可以由多个执行程序线程同时访问。
  • 使用寿命超过函数调用。

这意味着它应该是线程安全的,任何方法调用都应该是副作用。