当我遍历镶木地板文件和几个后期处理功能时,我一直试图弄清楚如何防止Spark因内存问题而崩溃。对于大量的文字感到抱歉,但这并不是一个特定的错误(我使用PySpark。)如果这打破了正确的Stack Overflow表单,请道歉!
基本伪代码是:
#fileNums are the file name partitions in the parquet file
#I read each one in as a separate file from its "=" subdirectory
for counter in fileNums:
sparkDataFrame = sqlContext.read.parquet(counter)
summaryReportOne = sqlContext.sql.("SELECT.....")
summaryReportOne.write.partition("id").parquet("/")
summaryReportTwo = sqlContext.sql.("SELECT....")
summaryReportTwo.write.partition("id").parquet("/")
#several more queries, several involving joins, etc....
此代码使用spark SQL查询,因此我在创建包含所有SQL查询/函数的包装函数并将其传递给foreach(无法将sparkContext或sqlQuery作为输入)时失败)而不是循环标准。
从技术上讲,这是一个有分区的大型木地板文件,但它可以同时读取并查询它。我需要在每个分区上运行这些功能。所以我只是在PySpark中运行一个常规的python循环,在每个循环中,我处理一个镶木地板分区(子目录)并编写相关的输出报告。
由于整个镶木地板文件的大小,不确定是否将大型mapPartition()周围的所有代码包装起来?
但是在几次循环之后,脚本因内存错误而崩溃 - 特别是Java堆错误。 (我已经确认,循环崩溃的文件没有什么特别之处;它发生在第二个或第三个循环中读入的任何随机文件。)
Caused by: com.google.protobuf.ServiceException:
java.lang.OutOfMemoryError: Java heap space
at org.apache.hadoop.ipc.ProtobufRpcEngine$Invoker.invoke(ProtobufRpcEngine.java:244)
at com.sun.proxy.$Proxy9.delete(Unknown Source)
at org.apache.hadoop.hdfs.protocolPB.ClientNamenodeProtocolTranslatorPB.delete(ClientNamenodeProtocolTranslatorPB.java:526)
... 42 more
Caused by: java.lang.OutOfMemoryError: Java heap space
我意识到Spark并不意味着要在循环中运行,但这些SQL查询对于标准的Spark SQL打包函数来说有点过于复杂,我们在不同的聚合上为每个文件写出多个摘要报告统计
有没有办法在每个循环索引的末尾基本清除内存?使用sqlContext.dropTempTable()删除任何已注册的临时表并使用sqlContext.clearCache()清除缓存并没有帮助。如果我尝试停止sparkContext并在每个循环中重新启动它,我也会收到错误,因为有些进程没有"包裹" (好像你曾经能够"优雅"停止上下文,但我无法在当前的PySpark文档中找到它。)
我还应该注意到,在我完成它之后,我并没有在循环中的数据帧上调用unpersist(),但我也没有在它们上调用persist();我只是重写每个循环中的数据帧(这可能是问题的一部分)。
我正在与我们的工程团队一起调整内存设置,但我们知道我们已经分配了足够的内存来完成此脚本的一个循环(并且一个循环运行时没有任何错误)。
任何建议都会有所帮助 - 包括可能比Spark更好用于此用例的工具。我正在使用Spark版本1.6.1。
答案 0 :(得分:1)
如果可以,请尝试升级到新发布的spark 2.0。
我遇到了与java堆空间非常相似的问题。通过简单地重复创建数据帧的过程并使用spark 1.6.2一次又一次地调用,我能够超过4G的堆空间。
使用Spark 2.0使用SparkSession,同一个程序只有1.2 GB的堆空间,并且内存使用率非常一致,正如我对运行的程序所期望的那样。
答案 1 :(得分:0)
更新:如果我在每个循环中使用sql查询后对每个表调用unpersist(),那么循环可以成功继续到下一次迭代而不会出现内存问题。如上所述,.clearCache()和单独删除临时表并没有成功。我猜这是有效的,因为虽然表来自sparkSQL查询,但它返回一个RDD。
即使我没有将persist()调用到这些RDD,我还是必须告诉Spark在下一个循环开始之前清除它们,以便我可以为这些相同的变量名分配新的SQL查询。