我正在尝试在Spark / Scala中编写一个函数,该函数在第一个中使用2个RDD和每个项目,从第二个中查找适合第一个日期范围内的项目。这是我写的用来表达问题的代码(我为了清晰起见添加了注释):
def buildRelationShip(sizeLogs: RDD[PerfLog], durationLog : RDD[PerfLog]) : RDD[(PerfLog, RDD[PerfLog])] =
{
durationLog.map((duration: PerfLog) => {
val sizes = sizeLogs.filter((size: PerfLog) => size.StartTime >= duration.StartTime && size.EndTime <= duration.EndTime)
(duration, sizes)
})
}
如果我在函数末尾的地图表达式上调用.collect(),我会得到这个异常。
15/06/19 15:57:05 ERROR Executor: Exception in task 0.0 in stage 3.0 (TID 3)
java.lang.NullPointerException
at org.apache.spark.rdd.RDD.filter(RDD.scala:282)
我发现如果我修改上面的代码以便在开始时收集这两个参数并将其作为数组用于其余的函数,那么它运行正常。
def buildRelationShip(sizeLogs: RDD[PerfLog], durationLog : RDD[PerfLog]) : Array[(PerfLog, Array[PerfLog])] =
{
val durationData = durationLog.collect()
val sizeData = sizeLogs.collect()
durationData.map((duration: PerfLog) => {
val sizes = sizeData.filter((size: PerfLog) => size.StartTime >= duration.StartTime && size.EndTime <= duration.EndTime)
(duration, sizes)
})
}
虽然这有效,但这显然不是正确答案,因为参数可能会变得非常大。
为什么它被视为一个数组而不是RDD?
答案 0 :(得分:2)
迭代一个RDD时不能迭代其他RDD。要解决这个问题,您不需要同时收集两个RDD,一个更好的解决方案来收集一个RDD(较小的一个,以获得更好的性能),然后使用这两个数据结构(RDD和Array)来获得n ^ 2操作。
def buildRelationShip(sizeLogs: RDD[PerfLog], durationLog : RDD[PerfLog]) : RDD[(PerfLog, Array[PerfLog])] =
{
val sizeData = sizeLogs.collect
durationLog.map((duration: PerfLog) => {
val sizes = sizeData.filter((size: PerfLog) => size.StartTime >= duration.StartTime && size.EndTime <= duration.EndTime)
(duration, sizes)
})
}
为了获得更好的性能,请使用Spark Broadcast。它实际上将变量广播到所有节点。如
def buildRelationShip(sizeLogs: RDD[PerfLog], durationLog : RDD[PerfLog]) : RDD[(PerfLog, Array[PerfLog])] =
{
val sizeData = sc.broadcast(sizeLogs.collect)
durationLog.map((duration: PerfLog) => {
val sizes = sizeData.value.filter((size: PerfLog) => size.StartTime >= duration.StartTime && size.EndTime <= duration.EndTime)
(duration, sizes)
})
}
希望它能帮到你。
答案 1 :(得分:1)
你不能把一个RDD放在另一个里面。 RDD只是指针只能在驱动程序上使用的日期。另一方面,地图是对工人进行的。