为什么不能在UDF中访问数据框? [Apache Spark Scala]

时间:2019-03-10 06:15:16

标签: scala apache-spark dataframe spark-structured-streaming

我目前正在使用Apache Spark进行流式项目。我有2个数据源,第一个是从Kafka获得新闻数据。该数据总是每次都在更新。第二个,我得到masterWord字典。此变量包含单词的数据帧和单词的唯一关键字。

我想处理新闻数据,然后通过将数据与masterWord词典进行匹配,将其从单词Seq转换为words_id的Seq。但是,在我的UDF中访问masterWord数据框时遇到问题。当我尝试访问UDF中的数据框时,Spark返回此错误

  

由以下原因导致:org.apache.spark.SparkException:由于阶段失败而导致作业中止:阶段4.0中的任务0失败了1次,最近一次失败:丢失了任务0.0 i   n阶段4.0(TID 4,本地主机,执行程序驱动程序):java.lang.NullPointerException

为什么不能在UDF中访问数据框?

从另一个数据框中获取价值的最佳实践是什么?

这是我的代码

// read data stream from Kafka
val kafka = spark.readStream
  .format("kafka")
  .option("kafka.bootstrap.servers", PropertiesLoader.kafkaBrokerUrl)
  .option("subscribe", PropertiesLoader.kafkaTopic)
  .option("startingOffsets", "earliest")
  .option("maxOffsetsPerTrigger", "100")
  .load()

// Transform data stream to Dataframe
val kafkaDF = kafka.selectExpr("CAST(value AS STRING)").as[(String)]
  .select(from_json($"value", ColsArtifact.rawSchema).as("data"))
  .select("data.*")
  .withColumn("raw_text", concat(col("title"), lit(" "), col("text"))) // add column aggregate title and text

// read master word dictionary
val readConfig = ReadConfig(Map("uri" -> "mongodb://10.252.37.112/prayuga", "database" -> "prayuga", "collection" -> "master_word_2"))
var masterWord = MongoSpark.load(spark, readConfig)

// call UDF
val aggregateDF = kafkaDF.withColumn("text_aggregate", aggregateMongo(col("text_selected")))

// UDF
val aggregateMongo = udf((content: Seq[String]) => {
  masterWord.show()
  ...
  // code for query masterWord whether var content exist or not in masterWord dictionary
})

2 个答案:

答案 0 :(得分:0)

数据帧位于spark上下文中,并且仅在驱动程序内部可用 每个任务都可以看到数据的一部分(分区),并且可以使用该部分。如果要使数据帧中的数据在udf内部可用,则必须将其序列化到主数据库,然后可以将其广播(或将其作为参数传递,本质上是一样的)到udf,在这种情况下,Spark会将整个内容发送到正在运行的udf的每个实例

答案 1 :(得分:0)

如果要在UDF中使用DataFrame,则必须创建Broadcast

import spark.implicits._

val df_name =Seq("Raphael").toDF("name")

val bc_df_name: Broadcast[DataFrame] = spark.sparkContext.broadcast(df_name)

// use df_name inside udf
val udf_doSomething = udf(() => bc_df_name.value.as[String].first())

Seq(1,2,3)
  .toDF("i")
  .withColumn("test",udf_doSomething())
  .show()

给予

+---+-------+
|  i|   test|
+---+-------+
|  1|Raphael|
|  2|Raphael|
|  3|Raphael|
+---+-------+

这至少在local模式下有效,请确保它是否也适用于群集。无论如何,我都不推荐这种方法,最好在驱动程序上的scala数据结构(例如,collect)中转换(Map)数据帧的内容并广播此变量,或者改用联接。 / p>