【问题标题】:Why caching small Spark RDDs takes big memory allocation in Yarn?为什么缓存小型 Spark RDD 需要在 Yarn 中分配大量内存?
【发布时间】:2021-04-01 09:07:31
【问题描述】:

缓存的RDD(共8个)并不大,只有30G左右,但是在Hadoop UI上,它显示Spark应用程序正在占用大量内存(没有活动作业running),即1.4T,为什么这么多?

为什么即使没有活动的作业正在运行,它也会显示大约 100 个执行程序(此处为 vCore)?

另外,如果缓存的 RDD 存储在 100 个 executor 中,这些 executor 是否会保留,其他 Spark 应用程序不能再使用它们来运行任务?换个说法:在执行程序中保留一点内存资源 (.cache) 会阻止其他 Spark 应用程序利用它们的空闲计算资源吗?

是否有任何潜在的 Spark 配置 / zeppelin 配置会导致这种现象?


更新 1

检查 Spark conf (zeppelin) 后,似乎有spark.executor.memory=10G 的默认设置(默认由管理员配置),这可能是原因。

但是,这里有一个新问题:是否可以只保留每个执行器中缓存的 RDD 所需的内存并释放其余部分,而不是始终保留最初设置的内存 spark.executor.memory=10G

火花配置

【问题讨论】:

  • 内存消耗可能有很多原因——缓存的 RDD 只是其中的一部分。如果你想减少内存使用,你可以尝试将spark.executor.memory设置为较低的值
  • @mck 谢谢。如果没有作业在运行并且缓存的 RDD 如此之小,还有什么可以将内存占用到 1.4T?
  • 以前运行的作业可能有一些未收集的垃圾
  • @mck 的确,这是鼓舞人心的提示,Spark 不应该自动进行 GC,还是有办法让用户触发 GC?我想这应该是一个普遍的问题,而且很严重,因为它在运行应用程序后占用了很多资源..
  • 我不确定,根据我的经验,它会在内存不足时收集垃圾。如果你给它 1T,它会一直使用它,直到空间用完。

标签: apache-spark hadoop hadoop-yarn apache-zeppelin


【解决方案1】:

也许您可以尝试在缓存之前将您的 RDD repartition(n) 分配到更少的 n < 100 分区。大约 30GB 的 RDD 可能适合十个 10GB 执行器的存储内存。可以在here 找到关于 Spark 内存管理的一个很好的概述。这样,只有那些持有缓存块的执行器会被“固定”到您的应用程序中,而其余的可以在 spark.dynamicAllocation.executorIdleTimeout(默认 60 秒)之后由 YARN 通过 Spark 动态分配回收。

问:是否可以只保留每个执行器中缓存的 RDD 所需的内存并释放其余部分,而不是始终保留最初设置的内存 spark.executor.memory=10G?

当 Spark 使用 YARN 作为其执行引擎时,YARN 为所有执行程序分配指定(按应用程序)大小的容器——至少 spark.executor.memory+spark.executor.memoryOverhead,但在 pyspark 的情况下可能更大。 Spark 实际使用了多少内存在容器内变得无关紧要,因为分配给容器的资源将被视为其他 YARN 应用程序的禁区。

【讨论】:

  • 感谢您的建议。我尝试了df.repartitionByRange(4, col("key")),然后触发了一个动作,但我仍然在“Storage” UI 中看到“Data Distribution on 100 Executors”,并且 Yarn 仍然显示 101 个 executor,知道为什么它无法重新分区吗?
  • 您确定您的操作是缓存重新分区的数据集,而不是重新分区缓存的数据集吗?顺序很重要... :)
  • unpersist缓存的df/rdd,repartitiondf/rdd和cache再次触发动作,那么它确实减少了nb。的执行者占用。更好的解决方案可能是使用coalesce,因为它应该避免不必要的 IMO 改组。
  • 有时,即使在使用repartition 并且只有1 个执行器持有缓存项之后,仍有20 个执行器处于ALIVE 状态,什么都不做!我应该明确杀死他们吗?为什么 Spark 不回收它们?这是预期的吗?
  • @jack,执行者还活着,因为当所有任务都完成后,该操作被标记为已完成。因此,如果一项任务有更多数据,其他任务将只是等待它完成。这就是为什么以某种方式重新分配数据很重要的原因。即使启用了动态分配,任务也不会被发送到其他动作,所以这里唯一的选择是设置数据均匀分布
【解决方案2】:

Spark 假设您的数据平均分布在所有执行程序和任务上。这就是您为每个任务设置内存的原因。所以为了让 Spark 消耗更少的内存,你的数据必须均匀分布:

  • 如果您正在读取 Parquet 文件或 CSV,请确保它们具有相似的大小。运行repartition() 会导致洗牌,如果数据如此倾斜,如果执行者没有足够的资源,可能会导致其他问题
  • 缓存无助于释放执行器上的内存,因为它不会重新分配数据
  • 您能否在舞台上的“事件时间线”下查看“绿色条有多大?”通常这与数据分布有关,因此这是一种查看每个任务(按比例)加载了多少数据以及它们正在执行多少数据的方法。由于所有任务都分配了相同的内存,因此您可以以图形方式查看资源是否被浪费(如果大部分是小条而大条很少)。下图显示了资源浪费示例

有多种方法可以为您的流程创建均匀分布的文件。我提到了一些可能性,但肯定还有更多:

  • 使用 Hive 和 DISTRIBUTE BY 子句:您需要使用一个同样平衡的字段才能创建与预期一样多的文件(并具有适当的大小)
  • 如果创建这些文件的进程是从数据库读取的 Spark 进程,请尝试根据需要创建尽可能多的连接,并使用适当的字段来填充 Spark 分区。正如herehere 的partitionColumn、lowerBound、upperBound 和numPartitions 属性所解释的那样
  • 重新分区可能会起作用,但请查看合并是否在您的进程中或在生成您正在从中读取的文件的前一个进程中也有意义

【讨论】:

  • @jack,没问题。看看你是否可以以任何方式重新分配你的数据,这样你就可以降低对执行程序的内存要求
  • 查看我在the answer above 中关于执行者是ALIVE 的评论,对此有什么想法吗?
  • 上面回复了,因为这是评论所在:)。谢谢你指点我!
猜你喜欢
  • 2018-05-12
  • 1970-01-01
  • 2016-12-13
  • 2018-02-07
  • 2019-04-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多