Scala stdout上的Spark-loss println()

Scala stdout上的Spark-loss println(),scala,apache-spark,println,accumulator,Scala,Apache Spark,Println,Accumulator,我有以下代码: val blueCount = sc.accumulator[Long](0) val output = input.map { data => for (value <- data.getValues()) { if (record.getEnum() == DataEnum.BLUE) { blueCount += 1 println("Enum = BLUE : " + value.toString() } }

我有以下代码:

val blueCount = sc.accumulator[Long](0)
val output = input.map { data =>
  for (value <- data.getValues()) {
    if (record.getEnum() == DataEnum.BLUE) {
      blueCount += 1
      println("Enum = BLUE : " + value.toString()
    }
  }
  data
}.persist(StorageLevel.MEMORY_ONLY_SER)

output.saveAsTextFile("myOutput")
val blueCount=sc.acculator[Long](0)
val output=input.map{data=>

对于(value而言,这是一个概念性问题

假设您有一个由许多工作者组成的大型集群,比如说
n
工作者,这些工作者存储
RDD
DataFrame
的分区,假设您在该数据上启动一个
map
任务,在该
map
中有一个
print
语句,首先:

  • 这些数据将在哪里打印出来
  • 哪个节点具有优先级,哪个分区
  • 如果所有节点并行运行,将首先打印谁
  • 如何创建此打印队列
这些问题太多了,因此
apachespark
的设计者/维护者从逻辑上决定放弃对
map reduce
操作中
print
语句的支持(这包括
累加器
甚至
广播
变量)

这也是有意义的,因为Spark是一种为非常大的数据集设计的语言。虽然打印对于测试和调试非常有用,但您不会希望打印数据帧或RDD的每一行,因为它们具有数百万或数十亿行!那么,当您甚至不想打印时,为什么还要处理这些复杂的问题呢第一名

为了证明这一点,您可以运行以下scala代码,例如:

// Let's create a simple RDD
val rdd = sc.parallelize(1 to 10000)

def printStuff(x:Int):Int = {
  println(x)
  x + 1
}

// It doesn't print anything! because of a logic design limitation!
rdd.map(printStuff)

// But you can print the RDD by doing the following:
rdd.take(10).foreach(println)

我可以通过制作一个实用函数来解决这个问题:

object PrintUtiltity {
    def print(data:String) = {
      println(data)
    }
}

我相信println工作得很好:它只会进入运行spark executor的计算机上的stdout/stderr。因此,除非你有办法捕获那些日志中的内容,否则你将永远看不到它。如果你使用的是Thread,那么有一个命令可以为你打印所有内容。尽管论证是有效的,spark不会执行任何类型的静态操作分析删除代码。正如@davideb所解释的那样,输出不会进入驱动程序
STDOUT
,因为Spark认为它调用的是一个实用函数,而不是打印函数。Spark显然没有(实际上也不能)检查其实用程序函数中的每一行。您所做的是在驱动程序中实例化一个对象。如果没有一个确切的模型,我不会指望这种行为。如果您的程序或调用PrintUtility对象的方式发生任何更改,预期行为会发生不可预测的变化。如果您想收集日志,使用标准方法来做,不要发明你不理解的随机机制。你对其工作原理的解释是危险的错误——没有禁止你做的事情;没有代码检查器来确保你不作弊:所有行为都遵循系统设计