我有一个带有关系的pyspark数据框和一个用于计算传递闭包的函数。我已经知道可以改善功能的几种方法(包括摆脱groupBy
),但让我们坚持下去。当我迭代地应用闭包函数时,计算时间呈指数增长,甚至火花用完堆内存。
from pyspark.sql.dataframe import DataFrame
from pyspark.sql.functions import col, min
def closure(eq: DataFrame) -> DataFrame:
eqrev = eq.select(col("ID2").alias("ID1"), col("ID1").alias("ID2"))
bi = eq.union(eqrev).distinct().cache()
oldCount = 0
nextCount = bi.count()
while True:
oldCount = nextCount
newEdges = bi.alias("b1").join(bi.alias("b2"), col("b1.ID1") == col("b2.ID1")).select(col("b1.ID2").alias("ID1"), col("b2.ID2"))
bi = bi.union(newEdges).distinct().cache()
nextCount = bi.count()
if nextCount == oldCount:
break
return bi.alias("b1").filter(col("b1.ID1") > col("b1.ID2")).groupBy("ID1").agg(min("ID2").alias("ID2")).cache()
b0 = sqlContext.createDataFrame([[ 22, 18 ], [ 20, 15] , [ 25, 26], [ 25, 29 ]], [ "ID1", "ID2" ])
b1 = closure(b0)
display(b1)
b2 = closure(b1)
display(b2)
b3 = closure(b2)
display(b3)
b4 = closure(b3)
b1
,b2
,b3
共有4行和200个分区(由join
引入)。执行计划线性增长:对于b4
,它是13个阶段。
在我的小型集群上,b2
的计算需要8秒钟,b3
的计算需要40秒钟,而b4
的计算在几分钟后给出java.lang.OutOfMemoryError: Java heap space
。
由于我正在缓存每个关闭的结果,因此我本来希望,火花引擎将能够解决这个问题。
一些相关文章:
Spark iteration time increasing exponentially when using join:批准的答案是说分区的数量呈指数增长。但这不是我的情况。它保持在200。
spark out of memory multiple iterations:建议使用localCheckpoint
如果将函数最后一行中的.cache()
更改为.localCheckpoint()
,则不会增加执行时间,也不会出现内存不足异常。 `localCheckPoint'的文档说: Checkpointing可用于截断此DataFrame的逻辑计划,这在计划可能呈指数增长的迭代算法中特别有用。本地检查点使用缓存子系统存储在执行程序中,因此它们不可靠。
我现在有以下问题:
我已经遇到了几乎没有数据的4次迭代的麻烦。真的可以预期吗?
为什么计算时间如此迅速地增加?为什么引擎的堆空间不足?执行计划仍然适合我的屏幕。
在失败的情况下使用localCheckPoint
有什么影响?