【问题标题】:NullPointer in broadcast value being passed as parameter广播值中的 NullPointer 作为参数传递
【发布时间】:2020-05-06 01:00:24
【问题描述】:

:)

我想说我是 Spark 的新手,因为其中许多帖子都开始了。但事实是我并不是那么新。 尽管如此,我仍然面临广播变量的这个问题。

当一个变量被广播时,每个执行器都会收到一份它的副本。稍后,当在执行器中执行的代码部分(比如说map或foreach)中引用该变量时,如果未将驱动程序中设置的变量引用传递给它,则执行器不知道是什么我们在谈论吗?我认为这是完美的解释here

我的问题是我得到了一个 nullPointerException,即使我将广播引用传递给了执行程序。

class A {
    var broadcastVal: Broadcast[Dataframe] = _
    ...

    def method1 {
        broadcastVal = otherMethodWhichSendBroadcast
        doSomething(broadcastVal, others)
    }
}

class B {
    def doSomething(...) {
        forEachPartition {x => doSomethingElse(x, broadcasVal)}
    }
}

object C {
    def doSomethingElse(...) {
        broadcastVal.value.show --> Exception
    }
}

我错过了什么?

提前致谢!

【问题讨论】:

    标签: apache-spark apache-spark-sql


    【解决方案1】:

    RDD 和 DataFrames 已经是分布式结构,不需要将它们作为局部变量广播。(org.apache.spark.sql.functions.broadcast() 函数(在进行连接时使用)不是局部变量广播)


    即使您明智地尝试代码语法,它也不会显示任何编译错误,而是会像NullPointerException 一样抛出RuntimeException,这是100% 有效的。

    解释行为的示例:

    package examples
    
    import org.apache.log4j.Level
    import org.apache.spark.broadcast.Broadcast
    import org.apache.spark.sql.{DataFrame, SparkSession}
    
    object BroadCastCheck extends App {
      org.apache.log4j.Logger.getLogger("org").setLevel(Level.OFF)
      val spark = SparkSession.builder().appName(getClass.getName).master("local").getOrCreate()
      val sc = spark.sparkContext
      val df = spark.range(100).toDF()
      var broadcastVal: Broadcast[DataFrame] = sc.broadcast(df)
    
    
      val t1 = sc.parallelize(0 until 10)
      val t2 = sc.broadcast(2) // this is right since its local variable can be primitive or map or any scala collection
      val t3 = t1.filter(_ % t2.value == 0).persist() //this is the way of ha
      t3.foreach {
        x =>
          println(x)
          // broadcastVal.value.toDF().show // null pointer  wrong way
        //   spark.range(100).toDF().show // null pointer  wrong way
    
      }
    }
    
    

    结果:(如果您在上面的代码中取消注释 broadcastVal.value.toDF().showspark.range(100).toDF().show

    Caused by: java.lang.NullPointerException
        at org.apache.spark.sql.execution.SparkPlan.sparkContext(SparkPlan.scala:56)
        at org.apache.spark.sql.execution.WholeStageCodegenExec.metrics$lzycompute(WholeStageCodegenExec.scala:528)
        at org.apache.spark.sql.execution.WholeStageCodegenExec.metrics(WholeStageCodegenExec.scala:527)
    

    进一步read the difference between broadcast variable and broadcast function here...

    【讨论】:

    • 感谢您这么快回答!我现在看到了,完全有道理#facePalmEmoji 我尝试广播 DF 的原因是因为我希望每个执行者都将它(整个)放在内存中,从而避免询问驱动程序或其他执行者。它是一个流式应用程序,延迟非常重要。但我认为最好收集 df,因为它很小,并且作为 MapObject 广播。你认为有更好的方法吗?
    • 如果它的数据帧很小/很小,记录数很少,那么它可以collect 并作为局部变量广播。否则,您将获得内存不足异常。因为 collect 会将整个数据移动到驱动程序......还有一些基于要求的其他技术。一种方法是缓存df.cache 将缓存在内存中。您可以在缓存的数据帧上使用连接(使用 largedf.join(broadcast(smalldf),Seq(keys to match for join ))来查找数据。它适用于 spark 核心和 spark 流。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-06-10
    • 1970-01-01
    • 2017-01-13
    • 1970-01-01
    相关资源
    最近更新 更多