我需要从一个大数据集中提取并转换一些信息,这些信息稍后将被其他数据集使用。
由于要使用的信息始终相同,并且可以以成对值的方式存储,因此我考虑将这些信息保存在udf会使用的外观映射中,因此我避免多次调用大型数据集。
问题是我遇到以下错误:
org.apache.spark.SparkException: Task not serializable
有什么方法可以使地图可序列化吗?
在不可能的情况下,是否还有另一种方法可以将信息存储在Spark中的查找对象中?
这是我的代码:
val cityTimeZone: scala.collection.immutable.Map[String,Double] = Map("CEB" -> 8.0, "LGW" -> 0.0, "CPT" -> 2.0
, "MUC" -> 1.0, "SGN" -> 7.0, "BNE" -> 10.0, "DME" -> 3.0, "FJR" -> 4.0, "BAH" -> 3.0, "ARN" -> 1.0, "FCO" -> 1.0, "DUS" -> 1.0, "MRU" -> 4.0, "JFK" -> -5.0, "GLA" -> 0.0)
def getLocalHour = udf ((city:String, timeutc:Int) => {
val timeOffset = cityTimeZone(city)
val localtime = if((timeutc+timeOffset)%24 >= 0)(timeutc+timeOffset)%24 else ((timeutc+timeOffset)%24)*(-1)
localtime
})
//$"dateutc" is a timestamp column like this: 2017-03-01 03:45:00 and $"city" a 3 letters code in capitals, like those in the map above
val newDF = DF
.select("dateutc","city")
.withColumn("utchour", hour($"dateutc"))
.withColumn("localhour", getLocalHour($"city", $"utchour"))
display(newDF)
答案 0 :(得分:1)
成员变量声明
val cityTimeZone
结合
cityTimeZone(city)
在udf
内的是有问题的,因为后者只是它的快捷方式
this.cityTimeZone(city)
其中this
(大概)是一些巨大的不可序列化对象(可能是因为它包含对不可序列化spark上下文的引用)。
将getLocalHour
设为lazy val
,然后将udf
所需的地图作为局部变量移动到getLocalHour
的定义中,大致如下: / p>
lazy val getLocalHour = {
val cityTimeZone: Map[String, Double] = Map("CEB" -> 8.0, "LGW" -> 0.0)
udf ((city:String, timeutc:Int) => {
val timeOffset = cityTimeZone(city)
val localtime = if((timeutc+timeOffset)%24 >= 0)(timeutc+timeOffset)%24 else ((timeutc+timeOffset)%24)*(-1)
localtime
})
}
或者,将cityTimeZone
附加到某些 serializable 对象(即一些不包含对任何线程,套接字,spark上下文和所有其他不可序列化的东西的引用的对象);例如package具有实用程序方法和常量的对象就可以了。
如果udf
定义包含对任何其他成员变量的引用,请相应地对其进行处理。