作业运行成功时,驱动程序内存,执行程序内存,驱动程序内存开销和执行程序内存开销的Apache Spark效果

时间:2015-08-30 01:46:02

标签: hadoop memory memory-management apache-spark out-of-memory

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

我将提供一些背景信息并发布我的问题并描述我之后经历过的案例。

我的环境设置如下:

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

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

问题

  1. 为什么在第一种情况和第二种情况之间抛出了不同的错误并且作业运行时间更长(第二种情况)只增加了执行程序内存?这两个错误是否以某种方式相关联?

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

  3.   

    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 个答案:

答案 0 :(得分:7)

您的所有案例都使用

--executor-cores 1

超过1是最好的做法。不要超过5。 根据我们的经验和Spark开发人员的推荐。

E.g。 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 bug,因为只是提供更多的内存并没有帮助,只需要切换到每个容器有更多的任务。我想同一个执行程序中的任务可能会在不同的时间达到峰值内存消耗,所以你不要浪费/不必为了让它工作而过度配置内存。

另一个好处是Spark的共享变量(累加器和广播变量)每个执行器只有一个副本,而不是每个任务 - 所以每个执行器切换到多个任务就是直接节省内存。即使您没有显式使用Spark共享变量,Spark也很可能在内部创建它们。例如,如果您通过Spark SQL连接两个表,Spark的CBO可能决定广播较小的表(或较小的数据帧)以使连接运行更快。

http://spark.apache.org/docs/latest/programming-guide.html#shared-variables