我目前正在处理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()
而不减少时间。
谢谢!
答案 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)。当您读取单个数据帧的谱系时,文件将自行读取。当然,成本是写入和读取文件。