【问题标题】:apache-spark memory consumption for cache() / persist()cache() / persist() 的 apache-spark 内存消耗
【发布时间】:2014-05-09 05:08:58
【问题描述】:

当我尝试缓存()或持久化(MEMORY_ONLY_SER())我的 RDD 时,我的 spark 集群挂起。它工作得很好,大约 7 分钟就可以计算出结果。如果我不使用 cache()。

我有 6 个 c3.xlarge EC2 实例(4 个内核,每个 7.5 GB RAM),总共提供 24 个内核和 37.7 GB。

我在 master 上使用以下命令运行我的应用程序:

SPARK_MEM=5g MEMORY_FRACTION="0.6" SPARK_HOME="/root/spark" java -cp ./uber-offline.jar:/root/spark/assembly/target/scala-2.10/spark-assembly_2.10-0.9 .0-incubating-hadoop1.0.4.jar pl.instream.dsp.offline.OfflineAnalysis

数据集是大约 50GB 的数据,分成 24 个文件。我将它压缩并存储在 24 个文件的 S3 存储桶中(每个文件的大小为 7MB 到 300MB)。

我绝对找不到集群出现这种行为的原因,但似乎 spark 消耗了所有可用内存并进入了 GC 收集循环。当我查看 gc 详细信息时,我可以找到如下循环:

[GC 5208198K(5208832K), 0,2403780 secs]
[Full GC 5208831K->5208212K(5208832K), 9,8765730 secs]
[Full GC 5208829K->5208238K(5208832K), 9,7567820 secs]
[Full GC 5208829K->5208295K(5208832K), 9,7629460 secs]
[GC 5208301K(5208832K), 0,2403480 secs]
[Full GC 5208831K->5208344K(5208832K), 9,7497710 secs]
[Full GC 5208829K->5208366K(5208832K), 9,7542880 secs]
[Full GC 5208831K->5208415K(5208832K), 9,7574860 secs]

这最终导致如下消息:

WARN storage.BlockManagerMasterActor: Removing BlockManager BlockManagerId(0, ip-xx-xx-xxx-xxx.eu-west-1.compute.internal, 60048, 0) with no recent heart beats: 64828ms exceeds 45000ms

...并停止计算的任何进展。这看起来内存消耗了 100%,但我尝试使用 RAM 更大的机器(比如每台 30GB),效果是一样的。

这种行为的原因可能是什么?有人可以帮忙吗?

【问题讨论】:

  • 您的应用程序持续执行 GC,但内存恢复非常少,某些配置/编码错误导致其填满内存
  • 是的 - 这也是我可以从日志中看到的。问题是它为什么会发生。
  • @Bartek 没有实际代码真的很难说。但是,50 GB 的文件很多。如果你一次加载所有这些并尝试处理,你当然会有严重的内存压力。
  • @Eugene 谢谢您的回复。据我了解,Apache-Spark 应该处理这么多的数据并将部分数据缓存在磁盘上,或者在出现内存问题时重新读取。我没有放代码,因为我不认为我的代码中有错误,这 1) 非常简单; 2)如果我不缓存/持久化,工作正常。如果你觉得有用,我可以贴出来?
  • 尝试缓存一些数据,比如说100万的样本,在Spark web UI上查看内存是如何消耗的,然后你应该找到你需要的总内存。

标签: java garbage-collection apache-spark


【解决方案1】:

尝试使用更多分区,每个 CPU 应该有 2 - 4 个。 IME 增加分区数量通常是使程序更稳定(通常更快)的最简单方法。

默认情况下,我认为您的代码将使用 24 个分区,但对于 50 GB 的数据来说太少了。我至少会尝试几个 100 个分区。

接下来您使用SPARK_MEM=5g,但假设每个节点有 7.5 GB,那么您不妨使用SPARK_MEM=7500m

您也可以尝试增加内存分数,但我认为上述方法更有可能有所帮助。

一般要点:对文件使用 HDFS 而不是 s3,它的速度要快得多。确保在缓存数据之前正确处理数据 - 例如如果您说 TSV 数据有 100 列,但您只使用了 10 个字段,请确保在尝试缓存之前已提取这些字段。

【讨论】:

  • 谢谢,coalesce 听起来是个不错的尝试方向。我会检查它并让你知道结果。 coalesce(500, true) 可能是一个不错的选择吗?
  • 在 cache() 帮助之前添加 coalesce(500, true) 。看来 GC 问题已经解决了。
  • 您对 24 个默认分区的假设从何而来? S3中的文件数?内核总数?
  • @EricEijkelenboom 是的,文件数。
【解决方案2】:

“原始”缓存和“序列化”缓存之间存在很大区别

  1. 原始缓存:(rdd.cache()rdd.persist(org.apache.spark.storage.StorageLevel.MEMORY_ONLY)

    这将消耗 2x-3x 的内存。例如,一个 100MB 的 rdd 会消耗 350MB 的内存

  2. 序列化缓存(rdd.persist(org.apache.spark.storage.StorageLevel.MEMORY_ONLY_SER))

    这消耗几乎相同数量的内存加上一些小的开销。例如 100MB 数据将消耗 100MB + 几 KB 的内存。

原始缓存在操作过程中的速度如何更快。序列化缓存需要更长的时间(因为在计算之前必须对对象进行反序列化)

这是来自我的experiment 的有趣结果。

【讨论】:

  • 如果您发现确实有重复,请将它们标记出来。
猜你喜欢
  • 1970-01-01
  • 2016-06-29
  • 1970-01-01
  • 2015-11-30
  • 1970-01-01
  • 1970-01-01
  • 2010-10-12
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多