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

Apache spark 在具有序列化问题的数据帧上调用UDF,apache-spark,Apache Spark,我在博客上看到了一些UDF的例子,这些例子看起来很有效,但事实上,当我运行它们时,它们给出了臭名昭著的TaskNotSerializable错误 我觉得奇怪的是,这篇文章发表了,却没有提到这一点。运行火花2.4 代码,直截了当地说火花里一定有什么变化了 def lowerRemoveAllWhitespace(s: String): String = { s.toLowerCase().replaceAll("\\s", "") } val lowerRemoveAllWhitespaceU

我在博客上看到了一些UDF的例子,这些例子看起来很有效,但事实上,当我运行它们时,它们给出了臭名昭著的TaskNotSerializable错误

我觉得奇怪的是,这篇文章发表了,却没有提到这一点。运行火花2.4

代码,直截了当地说火花里一定有什么变化了

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)
返回:

org.apache.spark.SparkException: Task not serializable
从这个博客中,我发现很好:

一定有什么改变了

我在这里用一个对象查看了排名靠前的项目,并扩展了Serializable,但也没有乐趣。困惑

编辑

情况似乎发生了变化,需要以下格式:

val squared = udf((s: Long) => s * s)  

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

发布的示例来自一个声誉良好的源,但如果Spark 2.4中没有序列化错误,我无法运行,尝试对象等也没有帮助

我使用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 native、列UDF的整个讨论在事情似乎不再有效时令人困惑。

我无法重现错误(在Spark 1.6、2.3和2.4上尝试过),但我确实记得(很久以前)遇到过这种错误。我将给出我的最佳猜测

这个问题是由于scala中的方法和函数之间的差异造成的

它的简短版本是当您编写
def
时,它相当于java中的方法,即类的一部分,可以使用类的实例调用

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


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

你能提到你正在使用的spark的版本吗?我没有发现api docs2.4中的
createDF
方法是版本,有些东西已经更改,文档不清楚。我想要一个多行函数,但这似乎很难。我想这是一个问题,手册似乎不一致。这不是问题不是支持什么,而是你如何构造你的代码(顺便说一句,你的问题中缺少了这一点,所以除非有人看到你的前一个问题,否则它没有多大意义。是否要包含?),将所有代码转储到对象体中的想法是不好的。不过,如果您想快速破解,只需将函数替换为
val lowerRemoveAllWhitespace=(s:String)=>{s.toLowerCase().replaceAll(“\\s”,”)}
这篇帖子暗示它在过去使用过Scala。我没有理由怀疑这一点,所以在实现上确实会发生变化。可能是这样吗?在我看来,Scala很像。我不确定他们使用的spark版本。
createDF
方法不是spark 1.6、2.3或2.4的一部分。但这个问题与spark版本无关。我我不记得导致异常的具体情况。但是我记得在本地模式下运行时,您不会看到这一点(所有工作人员都在同一个JVM中)所以没有序列化发生。然而,当我使用spark shell和Thread客户端模式时,我曾经看到过这一点。在DataBricks上,你也看到了。我非常确定在许多情况下,在本地spark shell上也会发生序列化。我现在正在看它-mapR。我想你可能会接受我的观点,这里有些地方不太清楚。作为一个大数据程序员并非适用于所有人。我的猜测是,只有当您以Thread客户端模式运行交互式shell(spark shell、pyspark、zepplin notebook、jupyter notebook)时才会发生这种情况。因为您键入的代码必须编译并序列化,然后发送到工作节点/进程