Caching 当我重复使用旧缓存数据的时间过长时,Spark Dataframe突然变得非常慢

Caching 当我重复使用旧缓存数据的时间过长时,Spark Dataframe突然变得非常慢,caching,apache-spark,dataframe,iteration,reusability,Caching,Apache Spark,Dataframe,Iteration,Reusability,当我试图将缓存的结果保存在一个列表中,并试图通过每次迭代中最后一个列表中的所有数据来计算新的DataFrame时,出现了这个问题。然而,即使我每次使用一个空数据帧并得到一个空结果,函数在大约8~12轮后会突然变得非常慢 这是我的密码 testLoop(Nil) def testLoop(lastDfList:List[DataFrame]){ // do some dummy transformation like union and cache the result val

当我试图将缓存的结果保存在一个列表中,并试图通过每次迭代中最后一个列表中的所有数据来计算新的DataFrame时,出现了这个问题。然而,即使我每次使用一个空数据帧并得到一个空结果,函数在大约8~12轮后会突然变得非常慢

这是我的密码

testLoop(Nil)
def testLoop(lastDfList:List[DataFrame]){      
  // do some dummy transformation like union and cache the result
  val resultDf = lastDfList.foldLeft(Seq[Data]().toDF){(df, lastDf) => df.union(lastDf)}.cache        

  // always get 0, of course  
  println(resultDf.count)  

  // benchmark action
  benchmark(resultDf.count)    

  testLoop(resultDf::lastDfList)
}
基准结果

1~6轮:<200ms
7轮:367毫秒
8轮:918ms
9轮:2476ms
10轮:7833毫秒
11轮:24231ms

我不认为GC或块逐出是我的问题,因为我已经使用了一个空的数据帧,但我不知道原因是什么?我是否误解了缓存或其他东西的含义

谢谢


在阅读了ImDarrenG的解决方案后,我将代码更改为:

spark.sparkContext.setCheckpointDir("/tmp")

testLoop(Nil)
def testLoop(lastDfList:List[DataFrame]){      
  // do some dummy transformation like union and cache the result
  val resultDf = lastDfList.foldLeft(Seq[Data]().toDF){(df, lastDf) => df.union(lastDf)}.cache        

  resultDf.checkpoint()  

  // always get 0, of course  
  println(resultDf.count)  

  // benchmark action
  benchmark(resultDf.count)    

  testLoop(resultDf::lastDfList)
}

但是经过几次迭代后,它仍然会变得非常慢。

在这里,通过将
resultDf
添加到
lastDfList
的开头,创建一个
DataFrames
列表,并将其传递到
testLoop
的下一次迭代:

testLoop(resultDf::lastDfList)
因此,
lastDfList
每次通过都会变长

此行通过
union
ing
lastDfList
的每个成员创建一个新的
DataFrame

val resultDf = lastDfList.foldLeft(Seq[Data]().toDF){(df, lastDf) => df.union(lastDf))}.cache 
lastDfList
的每个成员都是其前辈的联合体,因此,Spark保持着一个血统,随着
testLoop
的每一次传递,这个血统将以指数级增长

我预计时间的增加是由DAG的内务管理造成的。缓存数据帧消除了重复转换的需要,但沿袭仍然必须由spark维护

无论是缓存数据还是否,通过每次通过
testLoop
将每个
DataFrame
与它的所有前辈联合起来,看起来您正在构建一个非常复杂的DAG


您可以使用
checkpoint
修剪沿袭,并引入一些检查以防止无限递归。

根据和,
checkpoint
将返回一个新数据集,而不是更改原始数据集。

谢谢!你给我正确的方向。然而,甚至我也通过
spark.sparkContext.setCheckpointDir(“/tmp”)并在“``val resultDf=lastDfList.foldLeft(…){…}.cache``之后添加resultDf.checkpoint()。问题仍然存在,大约8次迭代后,速度将变得无法忍受。我可以在我的目录/tmp上看到这些检查点数据,但它们似乎没有帮助。看看
toDebugString
的输出,您所做的创建了一个非常复杂的DAG。考虑到代码的逻辑复杂性,可能会降低速度。我知道问题是,经过多次迭代后,我的DAG太复杂了,但API文档说,检查点可用于截断数据集的逻辑计划,因此我认为这可能有助于我的情况。然而,它不是:(,我查看了debugString中的信息,解释并看到DAG在每次迭代后仍然会越来越大。在24秒的时间内合并12个数据帧,缓存生成的数据帧,将其检查到磁盘,并执行收集操作,每个联合大约需要2秒的时间,虽然不是很快,但也不是很快感觉不合理。集群的大小(核心/内存/执行器)你在使用吗?我在我的本地机器上测试它-英特尔17,16G内存。2秒当然可以接受,但是,我的问题是,在大约7次迭代之后,时间将开始呈指数增长。我认为检查点方法应该解决增长的问题,并使每次迭代花费的时间几乎相同。