递归Dataframe操作

时间:2016-11-22 08:27:55

标签: scala hadoop apache-spark

在我的spark应用程序中,我想在循环中对数据帧进行操作,并将结果写入hdfs。

伪代码:

var df = emptyDataframe
for n = 1 to 200000{
  someDf=read(n)
  df = df.mergeWith(somedf)
}
df.writetohdfs

在上面的例子中,当" mergeWith"是一个unionAll。

然而,在" mergeWith"我做了一个(简单)加入,工作变得非常缓慢(> 1小时有2个执行器,每个有4个核心)并且永远不会完成(作业自行中止)。

在我的场景中,我使用仅包含~1mb文本数据的文件进行约50次迭代。

由于合并顺序在我的情况下很重要,我怀疑这是由于DAG的生成,导致整个事情在我存储数据时运行。

现在我试图在合并的数据框架上使用.persist,但这似乎也相当缓慢。

修改

当工作正在运行时,我注意到(即使我做了计数和.persist),内存中的数据帧看起来不像静态数据帧。 它看起来像是一直走在一起的所有合并路径,有效地线性减慢了工作。

我是否正确地认为var df是罪魁祸首?

spiraling out of controle

我发现问题的细分:

dfA = empty
dfC = dfA.increment(dfB)
dfD = dfC.increment(dfN)....

当我期待DF' C和D是对象,不同的东西,并不关心我是否坚持或重新分配。 Spark它看起来像这样:

dfA = empty
dfC = dfA incremented with df B
dfD = ((dfA incremented with df B) incremented with dfN)....

UPDATE2

为了摆脱坚持不工作的DF,我可以打破"将DF转换为RDD并再次返回时的谱系。 这有一点开销但是可以接受(工作在几分钟内完成,而不是几小时/从不) 我将对持久化进行更多测试,并以变通方式形成答案。

结果: 这似乎只是解决了表面上的这些问题。实际上,我回到第一个方位并获得OOM异常java.lang.OutOfMemoryError: GC overhead limit exceeded

2 个答案:

答案 0 :(得分:0)

如果你有这样的代码:

var df = sc.parallelize(Seq(1)).toDF()

for(i<- 1 to 200000) {
  val df_add = sc.parallelize(Seq(i)).toDF()
  df = df.unionAll(df_add)
}

然后df将有400000个分区,这使得以下操作效率低下(因为每个分区有1个任务)。

尝试将分区数量减少到例如在持久化数据帧之前200(使用例如df.coalesce(200).write.saveAsTable(....)

答案 1 :(得分:0)

以下是我最终使用的内容。它对我的用例来说足够高效,它有效并且不需要持久存在。

非常是一种解决方法,而非修复方法。

val mutableBufferArray = ArrayBuffer[DataFrame]()
mutableBufferArray.append(hiveContext.emptyDataframe())

for loop {

              val interm = mergeDataFrame(df, mutableBufferArray.last)
              val intermSchema = interm.schema
              val intermRDD = interm.rdd.repartition(8)


              mutableBufferArray.append(hiveContext.createDataFrame(intermRDD, intermSchema))
              mutableBufferArray.remove(0)

}

这就是我摔跤钨合规的方式。 通过从DF到RDD然后返回,我最终得到一个真实的物体而不是从前到后的整个钨生成的过程管道。

在我的代码中,我在写入磁盘之前迭代了几次(50-150次迭代看起来效果最好)。这就是我再次清除bufferArray以重新开始的地方。