静态分配

例子1: 硬件(6个节点,每个节点16个cores和64GB RAM)

首先,假设每个节点将有1个core和1GB内存用于操作系统和Hadoop的守护进程,因此每个节点还剩下15个cores和63GB内存。

先从core的数量选择开始,core的数量意味着一个executor能同时执行的tasks数量,也许我们认为高并行能够带来好的性能,但是研究表明超过5个并行任务会使性能下降,所以我们选择将core的数量设置为5个。无论一个node有多少个core,我们都将他设置成5,因为这取决于并行任务的性能而不是core的数量。

接着,如果将core数设置成5,那么每个节点可以跑3(15/5)个executors,在有6个节点的情况下,将会有18(3*6)个executors,其中一个将会被YARN的Application Master占用,所以最终可用于Spark任务的有17个executors。

由上可推出每个executor将会有21(63/3)GB,但是这其中包含了overhead memory,overhead memory等于384MB与0.07spark.executor.memory,在此例中则为 `max(384MB, 0.0721GB)=1.47GB`,因此最终每个executor的内存约等于19(21-1.47)GB。

所以,在该例中,共有17个executors,每个executor有5个cores和19GB的内存。

例子2: 硬件(6个节点,每个节点32个cores和64GB RAM)

core的数量同上,还是5个,则每个节点有exectuors为6(32/5)个,共有35 (6*6-1) _个exectuors,每个executor占内存10GB(63/6),overhead memory为约为1GB(0.07_10GB),所以最终每个executor的内存为9GB。

例子3: 当executor不需要过大的内存,大致能根据数据量和计算过程需要用到的内存算出每个executor所需的内存

上面第一个例子事先设定好了core的数量为5,得到每个executor有19GB,但如果数据量的大小和计算过程中的数据量仅仅需要10GB就足够,那么将core的数量有5设置为3(任何小于等于5的正整数),每个节点将会有5个executors,总共29(5_6-1)个executors,内存约为12(63/5)GB,算上overhead memory,最终将会得到29个executors,3个cores,内存约为11(12-12_0.07)GB。

动态分配

注意:如果是动态分配,executor的数量没有上限,所以Spark应用可能使用掉所有的资源,如果集群中还运行了其他的程序,我们应该注意资源的分配问题。因此应该为不同的用户设置不同的队列资源上限和下限,以保证不同的应用可以在YARN上正常运行。

spark.dynamicAllocation.enabled=true 时,在提交Spark任务时就不需要明确executors的数量了,Spark将通过以下参数进行动态设置:

  1. spark.dynamicAllocation.initialExecutors 用于设置初始化executor的数量;
  2. 随后,根据任务的执行等待情况,executor的数量将在 spark.dynamicAllocation.minExecutorsspark.dynamicAllocation.maxExecutors 范围内变化;
  3. 当有任务超过 spark.dynamicAllocation.schedulerBacklogTimeout 设置的等待时间时会申请新的executor资源,且申请的资源将成指数增长,直到达到最大值;
  4. 当一个executor的执行时间达到 spark.dynamicAllocation.executorIdleTimeout 时将释放资源。

总结

如果我们想更好的控制Spark任务的执行时间,监控任务的执行情况,应该选择静态分配模式。如果选择动态分配模式,资源分配将不再透明,且有可能影响集群上其他任务的执行。