为什么spark.memory.fraction的默认值这么低?

问题描述 投票:0回答:2

Spark 配置文档,我们了解以下关于

spark.memory.fraction
配置参数的信息:

用于执行和存储的(堆空间 - 300MB)的一部分。该值越低,溢出和缓存数据驱逐发生的频率就越高。此配置的目的是为内部元数据、用户数据结构以及稀疏、异常大的记录的情况下的不精确大小估计留出内存。建议将此值保留为默认值。

在撰写此问题时,此配置参数的默认值为 0.6。这意味着,对于具有 32GB 堆空间和默认配置的执行器,我们有:

  • 300MB
    保留空间(this 行上的硬编码值)
  • (32GB - 300MB) * 0.6 = 19481MB
    用于执行+存储的共享内存
  • 用户内存
  • (32GB - 300MB) * 0.4 = 12987MB

此“用户内存”(根据文档)用于以下用途:

其余空间 (40%) 保留用于用户数据结构、Spark 中的内部元数据,以及在记录稀疏和异常大的情况下防止 OOM 错误。

在具有 32GB 堆空间的执行器上,我们为此分配 12.7GB 内存,感觉相当大!

这些用户数据结构/内部元数据/防止 OOM 错误真的需要那么多空间吗?是否有一些引人注目的用户内存使用示例可以说明如此大的用户内存区域的需求?

apache-spark
2个回答
6
投票

我做了一些研究,我认为它的 0.6 不是为了确保用户内存有足够的内存,而是为了确保执行+存储可以适合 jvm 的旧代区域

在这里我发现了一些有趣的东西:Spark 调整

tenured Generation 大小由 JVM 的 NewRatio 控制 参数,默认为2,表示终身生成 新生代(堆的其余部分)大小的 2 倍。所以,通过 默认情况下,tenured Generation 占据 2/3 或大约 0.66 堆。 spark.memory.fraction 的值为 0.6 会保留存储空间并 老年代内的执行内存有空闲空间。如果 例如,spark.memory.fraction 增加到 0.8,那么 NewRatio 可能 必须增加到6个或更多。

因此,默认情况下,在 OpenJvm 中,该比率设置为 2,因此老一代有 0,66%,他们选择使用 0,6 来获得较小的利润

我发现在 1.6 版本中,它被更改为 0,75,并且导致了一些问题,这里是 Jira 票证

在描述中,您将找到示例代码,该代码将记录添加到缓存中,只是为了使用为执行+存储保留的整个内存。将存储+执行设置为比旧一代更高的量时,gc 的开销非常高,并且在旧版本(此设置等于 0.6)上执行的代码快了 6 倍(40-50 秒 vs 6 分钟)

经过讨论,社区决定在 Spark 2.0 中将其回滚到 0.6,这是 PR 的更改

我认为如果你想提高一点性能,你可以尝试将其更改为0.66,但如果你想有更多的内存用于执行+存储,你还需要调整你的jvm并更改旧/新比率,否则您可能会遇到性能问题


0
投票

让我们举一个玩具例子:

1. Read a CSV
2. Repartition to bring together the keys on the same executor
3. Add a transformation column (let us say the percentile within each key by which you repartitioned)
4. (optional) - cache this data
5. save the CSV to the disk

使用的记忆:

  1. 当您读取 csv 时,它会进入用户内存。因此默认 40% 是有道理的。您可以通过代码管理此内存
  2. 在重新分区期间,执行器需要某些数据结构(例如仅追加映射)来打乱数据。这些由 pyspark 内部管理,这就是使用执行内存的地方。您不通过代码管理此内存
  3. 如果您决定缓存数据,那么如果存储内存可用,则数据会缓存在存储内存中,因为执行内存一旦占用存储内存就无法被逐出(读取动态内存占用)

鉴于上面的玩具示例,我们可以看到所有内存分数比率(默认 - .4、.3、.3 用于用户、执行和存储)都是合乎逻辑的选择

© www.soinside.com 2019 - 2024. All rights reserved.