缓存时Spark堆外内存扩张

3

我正在努力让我的Spark程序避免超过YARN内存限制(在执行器上)。


我遇到的“已知”错误是:

Container killed by YARN for exceeding memory limits.  3.8 GB of 3.6 GB physical memory used.

我希望不仅仅提高执行器内存、执行器内存开销或调整我的资源或分区,我想知道为什么我的堆外内存总是在增长。

我正在使用 pyspark v2.4.4,据我所知 YARN 内存开销(对于执行器)是由 Spark 程序分配的任何堆外内存(JVM 之外)。

我唯一知道的额外的“非相关”的堆外内存是 Python 内存,它不是 Spark 的内存开销的一部分 documentation:

运行执行器的容器的最大内存大小取决于 spark.executor.memoryOverhead、spark.executor.memory、spark.memory.offHeap.size 和 spark.executor.pyspark.memory 的总和。

我在程序中使用了大量缓存;禁用persist调用可以在某种程度上解决内存开销问题(减少执行器死亡,最终使程序完成),但由于此数据应仅在JVM /磁盘中由UnifiedMemoryManager管理,因此不应使用任何堆外内存。
由于默认情况下禁用了堆外模式(spark.memory.offHeap.use),哪些情况可能导致内存开销增加,为什么禁用程序中的缓存有助于减少开销大小? 编辑
使用更大的执行器(两倍的内存和核心)也可以解决这个问题。
当JVM的内存更大时,开销更大是有道理的(大于386m时增加10%),但我们仍然在此JVM上使用了2个核心(即2个任务),并且我不明白在哪些情况下会增加内存开销。
1个回答

3

Spark在shuffle和缓存块传输期间可以使用堆外内存,即使spark.memory.offHeap.use=false

这个问题也被提到了Spark Summit 2016(第4分05秒)。

Spark 3.0.0及以上版本

自Spark 3.0.0以来,可以通过设置spark.network.io.preferDirectBufs=false来禁用此行为。

Spark配置对此进行了简要解释:

如果启用,则共享分配器首选堆外缓冲区分配。堆外缓冲区用于减少Shuffle和缓存块传输期间的垃圾回收。对于堆外内存严格受限的环境中,用户可能希望关闭此功能以强制所有分配都在堆上进行。

Spark < 3.0.0

对于低于 3.0.0 版本的Spark,可以使用更大的执行程序并调整更高的内存开销来显著改善此问题,同时保持每个执行程序相同的分配内存和相同的总资源消耗。

示例:

spark.executor.cores=1
spark.executor.memory=2g

容器总内存:2g + 384m(最小开销)= 2.375g
每个核心的执行器内存: 2.375g
每个核心的JVM内存: 2g

After:

spark.executor.cores=4
spark.executor.memory=6g
spark.executor.memoryOverhead=3g

总容器内存:6g + 3g = 9g
每个核的执行器内存:2.25g
每个核的JVM内存:1.5g

如果您的容器被YARN杀死(就像在这个问题中),所提出的更改应该有所帮助,但请注意这是与其他事物的权衡:
每个核的整体JVM内存较低,因此您更容易遇到用户内存(主要是执行器中创建的对象)和Spark内存(执行内存和存储内存)的内存瓶颈。
超限的Spark内存通常会溢出到磁盘上(附加非相关的复杂性),从而牺牲性能,缺乏用户内存可能会导致执行器出现内存不足错误,所以要小心。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接