我的目标是处理通过调用SequenceFile
生成的一系列org.apache.spark.rdd.RDD[_].saveAsObjectFile(...)
个文件夹。我的文件夹结构类似于:
\MyRootDirectory
\Batch0001
_SUCCESS
part-00000
part-00001
...
part-nnnnn
\Batch0002
_SUCCESS
part-00000
part-00001
...
part-nnnnn
...
\Batchnnnn
_SUCCESS
part-00000
part-00001
...
part-nnnnn
我需要提取一些持久化数据,但是我的集合 - 无论是使用ListBuffer
,mutable.Map
还是任何其他可变类型,都会丢失范围并且似乎在每次迭代时都会被新建sequenceFile(...).foreach
以下概念证明会生成一系列“处理目录...”,然后重复“1:1”并且不会增加,正如我预期counter
和intList.size
所做的那样。
private def proofOfConcept(rootDirectoryName: String) = {
val intList = ListBuffer[Int]()
var counter: Int = 0
val config = new SparkConf().setAppName("local").setMaster("local[1]")
new File(rootDirectoryName).listFiles().map(_.toString).foreach { folderName =>
println(s"Processing directory $folderName...")
val sc = new SparkContext(config)
sc.setLogLevel("WARN")
sc.sequenceFile(folderName, classOf[NullWritable], classOf[BytesWritable]).foreach(f => {
counter += 1
intList += counter
println(s" $counter : ${intList.size}")
})
sc.stop()
}
}
输出:
"C:\Program Files\Java\jdk1.8.0_111\bin\java" ...
Processing directory C:\MyRootDirectory\Batch0001...
17/05/24 09:30:25.228 WARN [main] org.apache.hadoop.util.NativeCodeLoader: Unable to load native-hadoop library for your platform... using builtin-java classes where applicable
[Stage 0:> (0 + 0) / 57] 1 : 1
1 : 1
1 : 1
1 : 1
1 : 1
1 : 1
1 : 1
1 : 1
Processing directory C:\MyRootDirectory\Batch0002...
1 : 1
1 : 1
1 : 1
1 : 1
1 : 1
1 : 1
1 : 1
1 : 1
Processing directory C:\MyRootDirectory\Batch0003...
1 : 1
1 : 1
1 : 1
1 : 1
1 : 1
1 : 1
1 : 1
1 : 1
答案 0 :(得分:1)
foreach
内的函数在spark worker JVM中运行,而不是在客户端JVM中运行,其中定义了变量。该worker在本地获取该变量的副本,递增它并打印它。我的猜测是你在本地进行测试吗?如果您在生产的分布式火花环境中运行它,您甚至看不到这些打印的输出。
更一般地说,几乎任何传递给RDD方法之一的函数都可能实际上是远程执行的,并且不会对任何局部变量或任何东西进行可变访问。它将获得一个基本上不可变的快照。
如果要将spark的分布式存储中的数据移回客户端,请使用RDD的collect
方法。相反的是sc.parallelize
。但请注意,这两种情况通常很少发生,因为它们不是并行发生的。