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对象的方式发生任何更改,预期行为会发生不可预测的变化。如果您想收集日志,使用标准方法来做,不要发明你不理解的随机机制。你对其工作原理的解释是危险的错误——没有禁止你做的事情;没有代码检查器来确保你不作弊:所有行为都遵循系统设计