首页 > 解决方案 > 为什么一个非常大的 Spark 阶段不使用所有可用的执行器?

问题描述

我正在运行一个包含一些非常大的阶段(例如 >20k 任务)的 Spark 作业,并且正在使用 1k 到 2k 执行器运行它。

在某些情况下,一个阶段似乎运行不稳定:许多可用的执行器随着时间的推移变得空闲,尽管仍然处于一个有许多未完成任务的阶段的中间。从用户的角度来看,任务似乎正在完成,但是已经完成给定任务的执行者不会获得分配给他们的新任务。结果,该阶段花费的时间超出了应有的时间,并且大量执行程序 CPU 小时被浪费在空闲上。这似乎主要(仅?)发生在从 HDFS 读取数据的输入阶段。

不稳定时期的 Spark stderr 日志示例 - 请注意,正在运行的任务数量会随着时间的推移而减少,直到几乎达到零,然后突然跳回到 >1k 正在运行的任务:

[Stage 0:==============================>                 (17979 + 1070) / 28504]
[Stage 0:==============================>                 (18042 + 1019) / 28504]
[Stage 0:===============================>                 (18140 + 921) / 28504]
[Stage 0:===============================>                 (18222 + 842) / 28504]
[Stage 0:===============================>                 (18263 + 803) / 28504]
[Stage 0:===============================>                 (18282 + 786) / 28504]
[Stage 0:===============================>                 (18320 + 751) / 28504]
[Stage 0:===============================>                 (18566 + 508) / 28504]
[Stage 0:================================>                (18791 + 284) / 28504]
[Stage 0:================================>                (18897 + 176) / 28504]
[Stage 0:================================>                (18940 + 134) / 28504]
[Stage 0:================================>                (18972 + 107) / 28504]
[Stage 0:=================================>                (19035 + 47) / 28504]
[Stage 0:=================================>                (19067 + 17) / 28504]
[Stage 0:================================>               (19075 + 1070) / 28504]
[Stage 0:================================>               (19107 + 1039) / 28504]
[Stage 0:================================>                (19165 + 982) / 28504]
[Stage 0:=================================>               (19212 + 937) / 28504]
[Stage 0:=================================>               (19251 + 899) / 28504]
[Stage 0:=================================>               (19355 + 831) / 28504]
[Stage 0:=================================>               (19481 + 708) / 28504]

这是一个阶段稳定运行时 stderr 的样子——运行任务的数量大致保持不变,因为新任务在执行者完成之前的任务时被分配给它们:

[Stage 1:===================>                            (11599 + 2043) / 28504]
[Stage 1:===================>                            (11620 + 2042) / 28504]
[Stage 1:===================>                            (11656 + 2044) / 28504]
[Stage 1:===================>                            (11692 + 2045) / 28504]
[Stage 1:===================>                            (11714 + 2045) / 28504]
[Stage 1:===================>                            (11741 + 2047) / 28504]
[Stage 1:===================>                            (11771 + 2047) / 28504]
[Stage 1:===================>                            (11818 + 2047) / 28504]

在什么情况下会发生这种情况,我该如何避免这种行为?

注意:我正在使用动态分配,但我很确定这与这个问题无关——例如,在不稳定时期,在 Spark Application Master UI 中,我可以看到预期的执行器数量是“活动的”,但是没有运行“活动任务”。

标签: apache-spark

解决方案


当每个任务花费的时间非常低时,我已经从 spark 中看到过这样的行为。出于某种原因,调度程序似乎假设作业将更快地完成而没有额外的分发开销,因为每个任务都完成得如此之快。

有几件事可以尝试:

  • 尝试.coalesce()减少分区的数量,以便每个分区需要更长的时间来运行(当然,这可能会导致 shuffle 步骤并且可能会增加整体作业时间,您必须过期)
  • 在此处调整spark.locality.wait*设置。如果每个任务花费的时间少于默认的等待时间,那么调度程序可能只是试图保持现有的槽满,而永远没有机会分配更多的槽。3s

我还没有确切地找到导致这个问题的原因,所以这些只是基于我自己在我自己的(小得多的)集群中的观察的推测和预感。


推荐阅读