我正在看一些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
对象方法仍然让我感兴趣,为什么它失败了。
答案 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的整个讨论令人困惑。