因此,我尝试使用Azure Databricks创建一个非常简单的spark笔记本,并希望使用简单的RDD地图调用。
这只是为了搞乱,所以这个例子有点做作,但我无法在RDD地图调用中获得值,除非它是一个静态常量值
我尝试过使用广播变量
这是一个使用int的简单示例,我在广播然后尝试在RDD地图中使用
val sparkContext = spark.sparkContext
val sqlContext = spark.sqlContext
import sqlContext.implicits._
val multiplier = 3
val multiplierBroadcast = sparkContext.broadcast(multiplier)
val data = Array(1, 2, 3, 4, 5)
val dataRdd = sparkContext.parallelize(data)
val mappedRdd = dataRdd.map(x => multiplierBroadcast.value)
val df = mappedRdd.toDF
df.show()
这是另一个例子,我使用简单的可序列化单例对象与int字段,我广播然后尝试在RDD映射中使用
val sparkContext = spark.sparkContext
val sqlContext = spark.sqlContext
import sqlContext.implicits._
val multiplier = 3
object Foo extends Serializable { val theMultiplier: Int = multiplier}
val fooBroadcast = sparkContext.broadcast(Foo)
val data = Array(1, 2, 3, 4, 5)
val dataRdd = sparkContext.parallelize(data)
val mappedRdd = dataRdd.map(x => fooBroadcast.value.theMultiplier)
val df = mappedRdd.toDF
df.show()
最后是一个List[int]
,其中包含我播放的单个元素,然后尝试在RDD地图中使用
val sparkContext = spark.sparkContext
val sqlContext = spark.sqlContext
import sqlContext.implicits._
val multiplier = 3
val listBroadcast = sparkContext.broadcast(List(multiplier))
val data = Array(1, 2, 3, 4, 5)
val dataRdd = sparkContext.parallelize(data)
val mappedRdd = dataRdd.map(x => listBroadcast.value.head)
val df = mappedRdd.toDF
df.show()
但是上述所有示例都会因此错误而失败。您可以看到哪一个指向RDD映射值不可序列化的问题。我看不出这个问题,并且int值应该可以使用我认为的所有上述示例进行序列化
org.apache.spark.SparkException: Task not serializable
at org.apache.spark.util.ClosureCleaner$.ensureSerializable(ClosureCleaner.scala:345)
at org.apache.spark.util.ClosureCleaner$.org$apache$spark$util$ClosureCleaner$$clean(ClosureCleaner.scala:335)
at org.apache.spark.util.ClosureCleaner$.clean(ClosureCleaner.scala:159)
at org.apache.spark.SparkContext.clean(SparkContext.scala:2375)
at org.apache.spark.rdd.RDD$$anonfun$map$1.apply(RDD.scala:379)
at org.apache.spark.rdd.RDD$$anonfun$map$1.apply(RDD.scala:378)
at org.apache.spark.rdd.RDDOperationScope$.withScope(RDDOperationScope.scala:151)
at org.apache.spark.rdd.RDDOperationScope$.withScope(RDDOperationScope.scala:112)
at org.apache.spark.rdd.RDD.withScope(RDD.scala:371)
at org.apache.spark.rdd.RDD.map(RDD.scala:378)
但是,如果我使RDD映射中的值成为像这样的常规int值
val sparkContext = spark.sparkContext
val sqlContext = spark.sqlContext
import sqlContext.implicits._
val data = Array(1, 2, 3, 4, 5)
val dataRdd = sparkContext.parallelize(data)
val mappedRdd = dataRdd.map(x => 6)
val df = mappedRdd.toDF
df.show()
一切正常,我看到我的简单DataFrame按预期显示
任何想法?
答案 0 :(得分:0)
从您的代码中,我会假设您使用的是Spark 2+。也许,没有必要下拉到RDD级别,而是使用DataFrames。
下面的代码显示了如何连接两个DataFrame并明确广播第一个。
import sparkSession.implicits._
import org.apache.spark.sql.functions._
val data = Seq(1, 2, 3, 4, 5)
val dataDF = data.toDF("id")
val largeDataDF = Seq((0, "Apple"), (1, "Pear"), (2, "Banana")).toDF("id", "value")
val df = largeDataDF.join(broadcast(dataDF), Seq("id"))
df.show()
通常,小型DataFrame是广播的理想候选者,作为优化,将它们发送给所有执行者。 spark.sql.autoBroadcastJoinThreshold 是一种限制适合广播的DataFrame大小的配置。其他详细信息可在Spark official documentation
上找到另请注意,使用DataFrames,您可以访问方便的 explain 方法。有了它,您可以看到物理计划,它可以用于调试。
在我们的示例中运行explain()将确认Spark正在进行 BroadcastHashJoin 优化。
df.explain()
== Physical Plan ==
*Project [id#11, value#12]
+- *BroadcastHashJoin [id#11], [id#3], Inner, BuildRight
:- LocalTableScan [id#11, value#12]
+- BroadcastExchange HashedRelationBroadcastMode(List(cast(input[0, int, false] as bigint)))
+- LocalTableScan [id#3]
如果您需要有关DataFrame的其他帮助,请在http://allaboutscala.com/big-data/spark/
提供大量示例列表答案 1 :(得分:0)
所以答案是你不应该在val中捕获Spark内容然后将其用于广播。所以这是工作代码
import sqlContext.implicits._
val multiplier = 3
val multiplierBroadcast = spark.sparkContext.broadcast(multiplier)
val data = Array(1, 2, 3, 4, 5)
val dataRdd = sparkContext.parallelize(data)
val mappedRdd = dataRdd.map(x => multiplierBroadcast.value)
val df = mappedRdd.toDF
df.show()
感谢@nadim Bahadoor的回答