Spark内置类型的Spark数据类型相等问题

时间:2019-04-11 06:22:52

标签: scala apache-spark apache-spark-sql

在运行spark应用程序时,我在催化剂深处发现错误。

例如:

java.lang.RuntimeException: scala.MatchError: LongType (of class org.apache.spark.sql.types.LongType$)
org.apache.spark.sql.catalyst.expressions.Cast.org$apache$spark$sql$catalyst$expressions$Cast$$nullSafeCastFunction(Cast.scala:637)
org.apache.spark.sql.catalyst.expressions.Cast.doGenCode(Cast.scala:625)
org.apache.spark.sql.catalyst.expressions.Expression$$anonfun$genCode$2.apply(Expression.scala:107)
org.apache.spark.sql.catalyst.expressions.Expression$$anonfun$genCode$2.apply(Expression.scala:104)
scala.Option.getOrElse(Option.scala:121)
org.apache.spark.sql.catalyst.expressions.Expression.genCode(Expression.scala:104)

在火花计划中,我将范围缩小到以下范围:

Project [if (isnull(_rawTime#348L)) null else UDF(toTime(_rawTime#348L)) AS _time#438,

(请注意,我无法控制架构为null,因为我是从spark hbase连接器获取此基础数据框的。)

toTime是一个UDF,它花费很长的时间并生成时间戳。即使match语句具有:

,看来催化剂无法匹配LongType
 case LongType => castToLongCode(from, ctx)

有趣的是,当我第一次运行此程序时,它运行良好。在第二次运行时,它存在此问题。

请注意,这是通过apache Livy运行的,因此执行之间的基本Spark会话应该相同。

我在工作开始时放置了以下代码。

  logger.info("----------")
  logger.info(LongType + " " + System.identityHashCode(LongType))
  logger.info(DataTypes.LongType + " " + System.identityHashCode(DataTypes.LongType))
  logger.info("Equal " + (DataTypes.LongType == LongType))
  logger.info("----------")

然后运行它,我看到:

first run:
----------
LongType 1044985410
LongType 1044985410
Equal true
----------
second run:
----------
LongType 355475697
LongType 1044985410
Equal false
----------

您可以看到在运行2中,对LongType的基于对象的调用与第一次运行时的标识不同。

Spark的注释建议人们使用DataTypes中的单例。例如{。{1}},因为看起来它们保持不变。但是,spark的代码使用非单例代码。

LongType定义为

DataTypes.LongType

/** * @since 1.3.0 */ @InterfaceStability.Stable case object LongType extends LongType

DataTypes.LongType

哪个引用前者(案例对象)。单例保持不变是有道理的。实际上,尽管内部火花代码的加载并未执行此操作,但是火花代码仍显示public static final DataType LongType = LongType$.MODULE$; DataTypes.LongType Please use the singleton..。对我来说,这就像个虫子。

Spark中的Scala代码可以正常编译,然后由于类型上的这种突然的标识更改而失败,这似乎很奇怪。

所以我的问题是:

  • 在Spark中使用.的建议是什么?我应该使用单例还是非单例?
  • 什么可能导致此身份在我下面改变?

1 个答案:

答案 0 :(得分:0)

我已经解决了这个问题。

基本上所有的DataType实例在Scala中都定义为:

 * @since 1.3.0
 */
@InterfaceStability.Stable
case object LongType extends LongType

但是...在许多地方,Spark使用Java代码通过单例获取DataType:

 * Gets the LongType object.
 */
public static final DataType LongType = LongType$.MODULE$;

LongType$.MODULE$;是如何从java领域调用案例对象。

但是我正在使用Kryo将DataType序列化为Livy,而Kryo正在内部重新初始化LongType$.MODULE$;。在Scala中,获得案例Object时获得的引用不是绑定到创建的第一个实例,而是绑定到创建的 last 实例。

因此时间表是:

  • 时间0:DataTypes.LongType的参考为1,LongType的参考为 1也。 (其中ref仅表示参考)
  • 时间1:Kryo反序列化,因此重新实例化了该对象。但是,单例DataTypes.LongType指向第一个实例。即DataTypes.LongType的参考值为1,LongType的参考值为2
  • 时间> = 2:随之而来-数据类型将无法通过相等性检查。

解决方案是不以这种方式将case对象传递给Kryo。可能由于某些原因,我们没有正确使用Kryo,或者我们需要使用twitter / chill。