我正在使用HDP 2.5,运行 spark-submit 作为纱线群集模式。
我尝试使用数据帧交叉连接生成数据。 即
val generatedData = df1.join(df2).join(df3).join(df4)
generatedData.saveAsTable(...)....
df1 存储级别为MEMORY_AND_DISK
df2,df3,df4 存储级别为MEMORY_ONLY
df1有更多的记录,即500万,而df2到df4最多有100条记录。 这样做,我的解释清楚将使用 BroadcastNestedLoopJoin 解释计划获得更好的性能。
由于某种原因,它总是失败。我不知道如何调试它以及内存爆炸的地方。
错误日志输出:
16/12/06 19:44:08 WARN YarnAllocator: Container marked as failed: container_e33_1480922439133_0845_02_000002 on host: hdp4. Exit status: 143. Diagnostics: Container killed on request. Exit code is 143
Container exited with a non-zero exit code 143
Killed by external signal
16/12/06 19:44:08 WARN YarnSchedulerBackend$YarnSchedulerEndpoint: Container marked as failed: container_e33_1480922439133_0845_02_000002 on host: hdp4. Exit status: 143. Diagnostics: Container killed on request. Exit code is 143
Container exited with a non-zero exit code 143
Killed by external signal
16/12/06 19:44:08 ERROR YarnClusterScheduler: Lost executor 1 on hdp4: Container marked as failed: container_e33_1480922439133_0845_02_000002 on host: hdp4. Exit status: 143. Diagnostics: Container killed on request. Exit code is 143
Container exited with a non-zero exit code 143
Killed by external signal
16/12/06 19:44:08 WARN TaskSetManager: Lost task 1.0 in stage 12.0 (TID 19, hdp4): ExecutorLostFailure (executor 1 exited caused by one of the running tasks) Reason: Container marked as failed: container_e33_1480922439133_0845_02_000002 on host: hdp4. Exit status: 143. Diagnostics: Container killed on request. Exit code is 143
Container exited with a non-zero exit code 143
Killed by external signal
在此错误之前,我没有看到任何WARN或ERROR日志。 问题是什么?我应该在哪里寻找内存消耗? 我在SparkUI的 存储 标签中看不到任何内容。 该日志取自HDP 2.5上的纱线资源管理器UI
修改
看着容器日志,好像是java.lang.OutOfMemoryError: GC overhead limit exceeded
我知道如何增加内存,但我已经没有任何记忆了。 如何在没有出现此错误的情况下使用4个Dataframe进行笛卡尔/产品连接。
答案 0 :(得分:4)
我也遇到了这个问题并尝试通过引用一些博客来解决它。 1.运行spark add conf bellow:
--conf 'spark.driver.extraJavaOptions=-XX:+UseCompressedOops -verbose:gc -XX:+PrintGCDetails -XX:+PrintGCTimeStamps' \ --conf 'spark.executor.extraJavaOptions=-XX:+UseCompressedOops -verbose:gc -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintHeapAtGC ' \
Heap after GC invocations=157 (full 98): PSYoungGen total 940544K, used 853456K [0x0000000781800000, 0x00000007c0000000, 0x00000007c0000000) eden space 860160K, 99% used [0x0000000781800000,0x00000007b5974118,0x00000007b6000000) from space 80384K, 0% used [0x00000007b6000000,0x00000007b6000000,0x00000007bae80000) to space 77824K, 0% used [0x00000007bb400000,0x00000007bb400000,0x00000007c0000000) ParOldGen total 2048000K, used 2047964K [0x0000000704800000, 0x0000000781800000, 0x0000000781800000) object space 2048000K, 99% used [0x0000000704800000,0x00000007817f7148,0x0000000781800000) Metaspace used 43044K, capacity 43310K, committed 44288K, reserved 1087488K class space used 6618K, capacity 6701K, committed 6912K, reserved 1048576K }
PSYoungGen和ParOldGen都是99%,然后你会得到java.lang.OutOfMemoryError:超出GC开销限制 如果创建了更多对象。
当有更多内存资源可用时,尝试为执行程序或驱动程序添加更多内存:
- executor-memory 10000m \
--driver-memory 10000m \
对于我的情况:PSYoungGen的内存小于ParOldGen,导致许多年轻对象进入ParOldGen内存区域并最终 ParOldGen不可用。所以java.lang.OutOfMemoryError:出现Java堆空间错误。
为执行者添加conf:
' spark.executor.extraJavaOptions = -XX:NewRatio = 1 -XX:+ UseCompressedOops -verbose:gc -XX:+ PrintGCDetails -XX:+ PrintGCTimeStamps'
-XX:NewRatio =率 rate = ParOldGen / PSYoungGen
它依赖。您可以尝试GC战略,如
-XX:+UseSerialGC :Serial Collector
-XX:+UseParallelGC :Parallel Collector
-XX:+UseParallelOldGC :Parallel Old collector
-XX:+UseConcMarkSweepGC :Concurrent Mark Sweep
Java Concurrent and Parallel GC
答案 1 :(得分:3)
所有容器的日志文件和我可以使用
yarn logs -applicationId application_1480922439133_0845_02
如果您只想要AM日志,
yarn logs -am -applicationId application_1480922439133_0845_02
如果要查找为此作业运行的容器,
yarn logs -applicationId application_1480922439133_0845_02|grep container_e33_1480922439133_0845_02
如果您只想要一个容器日志,
yarn logs -containerId container_e33_1480922439133_0845_02_000002
要使这些命令起作用,必须将日志聚合设置为true,否则您必须从各个服务器目录中获取日志。
<强>更新强> 除了尝试交换之外,你无能为力,但这会降低性能。
GC开销限制意味着,GC已经快速连续运行,但无法恢复太多内存。唯一的原因是,编写的代码编写得很差并且有很多反向引用(这很可疑,因为你正在进行简单的连接),或者已经达到了内存容量。
答案 2 :(得分:0)
原因1
默认情况下,随机播放计数为200
。随机播放过多会增加程序的复杂性和崩溃的机会。尝试在Spark会话中控制随机播放的次数。我使用以下代码将计数更改为5
。
implicit val sparkSession = org.apache.spark.sql.SparkSession.builder().enableHiveSupport().getOrCreate()
sparkSession.sql("set spark.sql.shuffle.partitions=5")
此外,如果您正在使用数据框,并且没有对数据框进行重新分区,则将在单个执行程序中完成执行。如果只有1个执行程序运行一段时间,则该纱线将使其他执行程序关闭。稍后,如果需要更多的内存,尽管yarn会尝试重新调用其他执行程序,但有时执行程序不会出现,因此该过程可能会因内存溢出问题而失败。为了克服这种情况,请在调用操作之前尝试对数据帧进行重新分区。
val df = df_temp.repartition(5)
请注意,您可能需要根据需要更改随机播放和分区计数。就我而言,上述组合有效。
原因2
它可能是由于未及时清除内存而发生的。例如,如果您正在使用Scala运行spark命令,并且正在执行一堆sql语句并导出到csv。一些配置单元表中的数据将非常庞大,您必须管理代码中的内存。
例如,请考虑以下代码,其中lst_Sqls
是包含一组sql命令的列表
lst_Sqls.foreach(sqlCmd => spark.sql(sqlCmd).coalesce(1).write.format("com.databricks.spark.csv").option("delimiter","|").save("s3 path..."))
运行此命令有时有时会遇到相同的错误。这是因为尽管spark清除了内存,但是它以一种惰性的方式进行操作,即,您的循环将继续进行,但是spark可能会在以后的某个时刻清除内存。
在这种情况下,您需要管理代码中的内存,即在执行每个命令后清除内存。为此,让我们很少更改代码。我在下面的代码中评论了每一行的作用。
lst_Sqls.foreach(sqlCmd =>
{
val df = spark.sql(sqlCmd)
// Store the result in in-memory. If in-memory is full, then it stored to HDD
df.persist(StorageLevel.MEMORY_AND_DISK)
// Export to csv from Dataframe
df.coalesce(1).write.format("com.databricks.spark.csv").save("s3 path")
// Clear the memory. Only after clearing memory, it will jump to next loop
df.unpersist(blocking = true)
})