为什么foreach没有为驱动程序带来任何东西?

时间:2015-03-02 07:32:21

标签: apache-spark

我在火花壳中编写了这个程序

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工作?

2 个答案:

答案 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 Executor 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) }

另见