我正面临在Spark 2.2.0中使用聚合和分区运行结构化流的内存问题:
session
.readStream()
.schema(inputSchema)
.option(OPTION_KEY_DELIMITER, OPTION_VALUE_DELIMITER_TAB)
.option(OPTION_KEY_QUOTE, OPTION_VALUE_QUOTATION_OFF)
.csv("s3://test-bucket/input")
.as(Encoders.bean(TestRecord.class))
.flatMap(mf, Encoders.bean(TestRecord.class))
.dropDuplicates("testId", "testName")
.withColumn("year", functions.date_format(dataset.col("testTimestamp").cast(DataTypes.DateType), "YYYY"))
.writeStream()
.option("path", "s3://test-bucket/output")
.option("checkpointLocation", "s3://test-bucket/checkpoint")
.trigger(Trigger.ProcessingTime(60, TimeUnit.SECONDS))
.partitionBy("year")
.format("parquet")
.outputMode(OutputMode.Append())
.queryName("test-stream")
.start();
在测试期间,我注意到每次新数据到来时最终使用的内存量都会增加,最后执行程序会以代码137退出:
ExecutorLostFailure (executor 2 exited caused by one of the running tasks) Reason: Container marked as failed: container_1520214726510_0001_01_000003 on host: ip-10-0-1-153.us-west-2.compute.internal. Exit status: 137. Diagnostics: Container killed on request. Exit code is 137
Container exited with a non-zero exit code 137
Killed by external signal
我创建了一个堆转储,发现org.apache.spark.sql.execution.streaming.state.HDFSBackedStateStoreProvider
引用的package hello;
import org.joda.time.LocalTime;
public class HelloWorld {
public static void main(String[] args) {
LocalTime currentTime = new LocalTime();
System.out.println("The current local time is: " + currentTime);
System.out.println("Hello World");
}
}
使用的大部分内存
乍一看它看起来很正常,因为这就是Spark在内存中保存聚合键的方式。但是我通过重命名源文件夹中的文件来进行测试,以便可以通过spark获取它们。由于输入记录是相同的,因此所有进一步的行都应该被拒绝作为重复行,并且内存消耗不应该增加,但它应该是。
此外,GC时间占总处理时间的30%以上
这是从执行程序中获取的堆转储,其运行内存量小于上面的屏幕,因为当我从该进程创建转储时,java进程刚刚在进程中间终止。
答案 0 :(得分:1)
迁移我对SPARK-23682的评论,该问题的提问者也已提出。
在HDFS状态存储提供程序中,它过多地将状态的多个版本缓存在内存中,默认为100个版本。 SPARK-24717解决了该问题,它将仅在内存中维护两个版本的状态(用于重放的当前版本,用于更新的新版本)。该补丁将在Spark 2.4.0中提供。
答案 1 :(得分:0)
我认为根本原因是您没有在dropDuplicates中使用水印,因此所有状态都将保留且永不丢弃。 看看:https://spark.apache.org/docs/latest/structured-streaming-programming-guide.html#streaming-deduplication