有效地在Spark中缓存大型数据帧

时间:2017-12-10 14:42:49

标签: scala apache-spark spark-dataframe

我目前正在处理11,000个文件。每个文件将生成一个数据帧,该数据帧将与前一个数据帧相连。以下是代码:

git fetch --all && git rebase ...

首先,我在11,000个文件上收到var df1 = sc.parallelize(Array(("temp",100 ))).toDF("key","value").withColumn("Filename", lit("Temp") ) files.foreach( filename => { val a = filename.getPath.toString() val m = a.split("/") val name = m(6) println("FILENAME: " + name) if (name == "_SUCCESS") { println("Cannot Process '_SUCCSS' Filename") } else { val freqs=doSomething(a).toDF("key","value").withColumn("Filename", lit(name) ) df1=df1.unionAll(freqs) } }) 错误。然后,我在java.lang.StackOverFlowError之后添加以下一行:

df1=df1.unionAll(freqs)

它解决了问题但在每次迭代后,它变得越来越慢。有人可以建议我应该做些什么来避免 df1=df1.cache() 而不减少时间。 谢谢!

1 个答案:

答案 0 :(得分:0)

问题是spark将数据帧作为一组转换进行管理。它始于" toDF"第一个数据帧,然后对它进行转换(例如withColumn),然后是unionAll与前一个数据帧等。

unionAll只是另一个这样的转换,树变得很长(11K unionAll你有一​​个深度为11K的执行树)。构建信息时unionAll可以进入堆栈溢出情况。

缓存并没有解决这个问题,但是,我想你正在添加一些动作(否则除了构建转换之外什么都不会运行)。当您执行缓存时,spark可能会跳过某些步骤,因此堆栈溢出只会稍后到达。

你可以回到RDD进行迭代过程(你的例子实际上不是迭代的,而是纯粹的并行,你可以简单地保存每个单独的数据帧,然后转换为RDD并使用RDD联合)。

由于您的案例似乎是在没有真正迭代的情况下联合一堆数据帧,您也可以以树的方式进行并集(即联合对,然后是对的联合对等),这将改变O的深度( N)到O(log N),其中N是联合数。

最后,您可以从磁盘读取和写入数据帧。这个想法是在每个X(例如20个)联合之后,你会做df1.write.parquet(filex)然后df1 = spark.read.parquet(filex)。当您读取单个数据帧的谱系时,文件将自行读取。当然,成本是写入和读取文件。