我在火花壳中编写了这个程序
val array = sc.parallelize(List(1, 2, 3, 4))
array.foreach(x => println(x))
这会打印一些调试语句,但不打印实际数字。
以下代码可以正常使用
for(num <- array.take(4)) {
println(num)
}
我明白take
是一个动作,因此会引发火花触发懒惰的计算。
但是foreach应该以同样的方式工作......为什么foreach
没有从火花中带回任何东西并开始进行实际处理(退出懒惰模式)
我怎样才能让rdd上的foreach工作?
答案 0 :(得分:32)
Spark中的RDD.foreach
方法在集群上运行,因此包含这些记录的每个工作程序都在foreach
中运行操作。即你的代码正在运行,但它们是在Spark worker stdout上打印出来的,而不是在驱动程序/你的shell会话中打印出来的。如果查看Spark worker的输出(stdout),您将看到这些打印到控制台。
您可以通过转到为每个正在运行的执行程序运行的web gui来查看worker上的stdout。示例网址为http://workerIp:workerPort/logPage/?appId=app-20150303023103-0043&executorId=1&logType=stdout
在此示例中,Spark选择将RDD的所有记录放在同一分区中。
如果您考虑一下这是有道理的 - 查看foreach
的函数签名 - 它不会返回任何内容。
/**
* Applies a function f to all elements of this RDD.
*/
def foreach(f: T => Unit): Unit
这实际上是scala中foreach
的目的 - 它用于副作用。
当你收集记录时,你把它们带回到驱动程序中,所以逻辑上收集/获取操作只是在Spark驱动程序中的Scala集合上运行 - 你可以看到日志输出,因为spark driver / spark shell是什么打印到stdout在你的会话中。
foreach的用例可能看起来不是很明显,例如 - 如果对于RDD中的每个记录你想做一些外部行为,比如调用REST api,你可以在foreach中执行此操作,然后每个Spark工作者将使用该值向API服务器提交调用。如果foreach确实带回了记录,你可以很容易地在驱动程序/ shell进程中烧掉内存。这样就可以避免这些问题,并且可以对集群中RDD中的所有项目产生副作用。
如果你想查看我使用的RDD中的最新信息,那么
array.collect.foreach(println)
//Instead of collect, use take(...) or takeSample(...) if the RDD is large
答案 1 :(得分:3)
您可以使用RDD.toLocalIterator()将数据带到驱动程序(一次一个RDD分区):
val array = sc.parallelize(List(1, 2, 3, 4))
for(rec <- array.toLocalIterator) { println(rec) }
另见