我想知道使用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)
})
}
有人可能会问为什么你甚至想要它...
我想创建一个通用的容器概念,用于在任何通用集合实现上运行我的逻辑(RDD
,List
,scalding pipe
等。)
所有这些都有" map"的概念,但mapPartition
对于spark
是唯一的。
答案 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
实例。结果这个例子:
这意味着它应该是线程安全的,任何方法调用都应该是副作用。