Apache Spark中Driver Memory、Executor Memory、Driver Memory Overhead和Executor Memory Overhead对作业运行成功的影响

26

我正在对我在YARN上运行的Spark作业进行一些内存调优,并且我注意到不同的设置会产生不同的结果并影响Spark作业运行的结果。然而,我感到困惑,不完全理解为什么会发生这种情况,如果有人能为我提供一些指导和解释,我将不胜感激。

我将提供一些背景信息并发布我的问题并描述下面我经历过的情况。

我的环境设置如下:

  • 每个节点20G内存,20个VCores(总共3个节点)
  • Hadoop 2.6.0
  • Spark 1.4.0

我的代码递归地过滤RDD以使其更小(作为算法的一部分删除示例),然后执行mapToPair并收集结果并将其保存在列表中。

问题

  1. 为什么仅增加执行器内存时第二种情况会抛出不同的错误并且作业运行时间较长?这两种错误是否有某种联系?

  2. 第三种和第四种情况都成功了,我明白这是因为我提供了更多的内存来解决内存问题。但是,在第三种情况下,

spark.driver.memory + spark.yarn.driver.memoryOverhead = YARN将创建的JVM内存

= 11g +(driverMemory * 0.07,最小为384m) = 11g + 1.154g = 12.154g

因此,从公式中,我可以看到我的作业需要 MEMORY_TOTAL 大约为12.154g 才能成功运行,这解释了为什么我需要10g以上的驱动器内存设置。

但是对于第四种情况,

spark.driver.memory + spark.yarn.driver.memoryOverhead = YARN将创建的JVM内存

= 2 + (driverMemory * 0.07, 至少为 384m) = 2g + 0.524g = 2.524g

似乎仅通过增加1024(1g)的内存开销,即可使具有仅2g驱动程序内存的作业成功运行,而 MEMORY_TOTAL 仅为2.524g !然而,如果没有增加内存开销的配置,则小于11g的驱动程序内存会失败,但是这不合理的,这就是我困惑的原因。

为什么增加内存开销(针对驱动程序和执行者)可以让我的作业成功完成,同时 MEMORY_TOTAL(12.154g vs 2.524g) 更低?我是否错过了一些其他内部工作?

第一种情况

/bin/spark-submit --class <class name> --master yarn-cluster --driver-memory 7g --executor-memory 1g --num-executors 3 --executor-cores 1 --jars <jar file>

如果我使用小于11g的任何驱动程序内存运行程序,则会出现下面的错误,这是SparkContext停止或类似错误(在停止SparkContext上调用的方法)。根据我收集的信息,这与内存不足有关。

DriverMemory-7g_ExecutorMemory-1g

第二种情况

/bin/spark-submit --class <class name> --master yarn-cluster --driver-memory 7g --executor-memory 3g --num-executors 3 --executor-cores 1 --jars <jar file>

如果我使用相同的驱动程序内存但更高的执行器内存运行程序,则作业运行时间较长(约3-4分钟),然后它将遇到与先前不同的错误,该错误是容器请求/使用的内存超过允许的限制,因此被杀死。尽管执行器内存增加了,但这个错误发生了,而不是第一种情况下的错误。

DriverMemory-7g_ExecutorMemory-3g

第三种情况

/bin/spark-submit --class <class name> --master yarn-cluster --driver-memory 11g --executor-memory 1g --num-executors 3 --executor-cores 1 --jars <jar file>

任何驱动程序内存大于10g的设置都将导致作业能够成功运行。

第四种情况

/bin/spark-submit --class <class name> --master yarn-cluster --driver-memory 2g --executor-memory 1g --conf spark.yarn.executor.memoryOverhead=1024 --conf spark.yarn.driver.memoryOverhead=1024 --num-executors 3 --executor-cores 1 --jars <jar file>

使用这个设置(驱动程序内存2g和执行器内存1g,但增加驱动程序内存开销(1g)和执行器内存开销(1g)),作业将成功运行。

非常感谢您的帮助,这将有助于我更好地理解Spark。提前感谢您的回复。

1个回答

8

您所有的案例都使用

--executor-cores 1

最佳做法是超过1,但不要超过5。这是基于我们的经验和Spark开发人员的建议。

例如:http://blog.cloudera.com/blog/2015/03/how-to-tune-your-apache-spark-jobs-part-2/

A rough guess is that at most five tasks per executor 
can achieve full write throughput, so it’s good to keep 
the number of cores per executor below that number

我找不到一份建议每个执行器超过1个内核的参考文献。但是,这个想法是在同一个执行器中运行多个任务可以共享一些公共内存区域,因此实际上可以节省内存。
从--executor-cores 2开始,将--executor-memory加倍(因为--executor-cores还告诉一个执行器将同时运行多少个任务),然后看看它对您有什么作用。您的环境在可用内存方面很紧凑,因此将其提高到3或4将使您的内存利用率更高。
我们使用Spark 1.5,并且停止使用--executor-cores 1已经有一段时间了,因为它会导致GC问题;看起来像是一个Spark错误,因为仅仅增加内存并没有像改变容器中的任务数量那样有帮助。我猜测在同一个执行器中的任务可能会在不同的时间达到其内存消耗峰值,因此您不必浪费/过度分配内存来使其工作。
另一个好处是,Spark的共享变量(累加器和广播变量)将每个执行器只有一个副本,而不是每个任务-所以切换到多个任务每个执行器直接节省内存。即使您不明确使用Spark共享变量,Spark很可能在内部创建它们。例如,如果您通过Spark SQL连接两个表,则Spark的CBO可能决定广播一个较小的表格(或较小的数据框)以使连接运行更快。 http://spark.apache.org/docs/latest/programming-guide.html#shared-variables

这不是有关驱动程序内存开销与驱动程序内存问题的答案,我认为这种行为仍然是一个未解决的问题。 - Boris Mitioglov

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