在具有序列化问题的数据帧上调用UDF

时间:2019-03-10 10:41:31

标签: apache-spark

我正在看一些UDF博客上的示例,这些示例似乎可以正常工作,但实际上,当我运行它们时,它们给出了臭名昭著的任务,而不是可序列化的错误。

我发现奇怪的是,这篇文章没有发表。运行Spark 2.4。

代码,很简单,Spark中的某些内容必须更改了吗?:

foo :-
    fact1(A,B,_),
    fact2(A,_),
    fact3(A,_),
    write(A),
    write(B),
    nl,
    fail.
foo.

返回:

def lowerRemoveAllWhitespace(s: String): String = {
  s.toLowerCase().replaceAll("\\s", "")
}
val lowerRemoveAllWhitespaceUDF = udf[String, String](lowerRemoveAllWhitespace)

import org.apache.spark.sql.functions.col
val df = sc.parallelize(Seq(
 ("r1 ", 1, 1, 3, -2),
 ("r 2", 6, 4, -2, -2),
 ("r 3", 4, 1, 1, 0),
 ("r4", 1, 2, 4, 5)
 )).toDF("ID", "a", "b", "c", "d")

df.select(lowerRemoveAllWhitespaceUDF(col("ID"))).show(false)

从这个博客中我发现不错:https://medium.com/@mrpowers/spark-user-defined-functions-udfs-6c849e39443b

某些东西必须改变了?

我在这里查看了带有对象的投票最高的项目,并扩展了可序列化,但也没有乐趣。困惑。

编辑

事情似乎已经改变,需要这种格式:

org.apache.spark.SparkException: Task not serializable

对象方法仍然让我感兴趣,为什么它失败了。

2 个答案:

答案 0 :(得分:1)

我无法重现该错误(在spark 1.6、2.3和2.4上尝试过),但我确实记得遇到过这种错误(很久以前)。我会尽我所能。

由于scala中的Method和Function之间的差异而发生问题。 As described in detail here

简短版本是当您编写def时,它等效于Java中的方法,即类的一部分,并且可以使用该类的实例进行调用。

编写udf((s: Long) => s * s)时会创建一个特征Function1的实例。为此,生成了一个实现Function1的匿名类,其应用方法类似于def apply(s: Long):Long= {s * s},该类的实例作为参数传递给udf

但是,当您编写udf[String, String](lowerRemoveAllWhitespace)时,方法lowerRemoveAllWhitespace需要转换为Function1实例并传递给udf。这是序列化失败的地方,因为此实例上的apply方法将尝试在导致异常的另一个对象(无法序列化并发送到worker jvm进程)的实例上调用lowerRemoveAllWhitespace

答案 1 :(得分:0)

发布的示例来自一个有信誉的来源,但是如果没有Spark 2.4中的序列化错误,我无法运行,尝试Objects等也无济于事。

我使用udf((..方法)解决了以下问题,该方法看起来像只有一条语句,实际上我可以做到这一点,不会序列化。尽管使用原语,这是一个略有不同的示例。

val sumContributionsPlus  = udf((n1: Int, n2: Int, n3: Int, n4: Int) => Seq(n1,n2,n3,n4).foldLeft(0)( (acc, a) => if (a > 0) acc + a else acc))

最后一点,当事情似乎不再起作用时,有关UDF(Spark本机)列UDF的整个讨论令人困惑。