一,如果不调节并行度,导致并行度过低,会导致什么?
假设,现在 spark-submit脚本里,给我们spark作业分配了足够多的资源,比如50个excutor ,每个excutor有10g内存,每个excutor,有3个core,最大并行度为50*3=150个task(executor数量*每个executor核数),150个执行完后,再换下一批150个task。
如果task没有设置,或者设置的很少,比如就设置了100个task。50个excutor,每个excutor有3个cpu core,也就是说,Application任何一个stage运行时,都有总数在150个cpu core,可以并行运行。但是只有100个task,平均分配,每个excutor分配2个task,那么导致每个excutor 剩余一个cpu core 浪费资源。
资源虽然足够,但是并行度没有与资源相匹配资源给多,会导致分配下去资源浪费掉。如果资源给少会降低性能(执行速度)。
(分配资源策略--给application分配资源选择worker(executor),现在有两种策略)
1.尽量的打散,即一个Application尽可能多的分配到不同的节点。这个可以通过设置spark.deploy.spreadOut来实现。默认值为true,即尽量的打散。(默认)
2.尽量的集中,即一个Application尽量分配到尽可能少的节点。
(spark性能优化--增加executor的内存量):
1.如果需要对RDD进行cache,那么更多的内存,就可以缓存更多的数据,将更少的数据写入磁盘,甚至不写入磁盘。减少了磁盘IO。
2.对于shuffle操作,reduce端,会需要内存来存放拉取的数据并进行聚合。如果内存不够,也会写入磁盘。如果给executor分配更多内存以后,就有更少的数据,需要写入磁盘,甚至不需要写入磁盘。减少了磁盘IO,提升了性能。
3.对于task的执行,可能会创建很多对象。如果内存比较小,可能会频繁导致JVM堆内存满了,然后频繁GC,垃圾回收, GC和full GC。(速度很慢)。内存加大以后,带来更少的GC,垃圾回收,避免了速度变慢,速度变快了。
二,怎么设置
一般我们通过配置这些Spark运行参数:--num-executors,,--executor-memory, --execuor-cores 来进行调节并行度,如何正确配置参数达到最优性能而不使资源浪费了?
三,理论引导
当配置参数时,请遵循下表,将其推荐建议牢记于心
Hadoop/Yarn/OS 守护进程:
当利用一个集群管理器(比如YARN)运行spark程序时,存在一些守护进程运行在后台,比如NameNode,Secondary NameNode,DataNode,JobTracker和TaskTracker。
因此,当确定num-executor时,我们需要确保有足够的cores(大约每个节点一个core)维持这些守护进程的平稳运行。
Yarn ApplicationMaster (AM):
ApplicationMaster的职责是:向ResourceManager协商资源,与NodeManager一同执行并监控containner及其资源消耗。如果程序运行在Spark-On-Yarn,我们需要预留一些资源给ApplicationMaster,AM大约需要1024MB的内存和一个Executor。
HDFS吞吐:
HDFS客户端会遇到大量并发线程的问题。 据观察,HDFS当达到全写入吞吐量时,需要每个executor执行约5个任务。 因此,最好控制每个executor中core的数目低于那个数字。
内存开销:
下图描绘了spark-yarn的内存使用情况:
[图片上传失败...(image-fa6806-1536392056623)]
图中注意两件事情:
Full memory requested to yarn per executor = spark-executor-memory + spark.yarn.executor.memoryOverhead spark.yarn.executor.memoryOverhead = Max(384MB, 7% of spark.executor-memory)
所以,如果我们申请了每个executor的内存为20G时,对我们而言,AM将实际得到20G+ memoryOverhead = 20 + 7% * 20GB = ~23G内存。
执行拥有太多内存的executor会产生过多的垃圾回收延迟
执行过小的executor(举例而言,一个只有一核和仅仅足够内存跑一个task的executor),将会丢失在单个JVM中运行多任务的好处。
四,开始实战
**集群配置:**
10个节点
每个节点16核
每个节点64G内存
第一种方案:使用较小的executors
Tiny executors表示一个executor配置一个core。下表描述了该方案下的参数配置。
- '--num-executors' = '该方案下,每个executor配置一个core' = '集群中的core的总数' = '每个节点的core数目 * 集群中的节点数' = 16 x 10 = 160 - '--executor-cores' = 1 (每个executor一个core) - '--executor-memory' = '每个executor的内存' = '每个节点的内存/每个节点的executor数目' = 64GB/16 = 4GB
第二种方案:使用较大的executors
Fat executors表示一个executor独占一个节点。下表描述了该方案下的参数配置:
- `--num-executors` = `该方案下,一个executor独占一个节点` = `集群中的节点的数目` = 10 - `--executor-cores` = `一个节点一个executor意味着每个executor独占节点中所 有的cores` = `节点中的core的数目` = 16 - `--executor-memory` = `每个executor的内存` = `节点的内存/节点中executor的数目` = 64GB/1 = 64GB
分析:每个executor独占16个核心,则ApplicationManager和守护程序进程则无法分配到core,并且,HDFS吞吐量会受到影响,导致过多的垃圾结果。 同样地,该方案不好!
第三种方案:优化executors(推荐)
考虑Linux运行及程序、Hadoop、Yarn等守护进程等,约占5%-10%,每台预留1核4G内存
基于上述建议,我们给每个executor分配5个core => -- executor-cores = 5 (保证良好的HDFS吞吐)
每个节点留一个core给Hadoop/Yarn守护进程 => 每个节点可用的core的数目 = 16 - 1
所以,集群中总共可用的core的数目是 15 * 10 = 150
可用的executor的数目 = (总的可用的core的数目 / 每个executor的core的数目)= 150 / 5 = 30
留一个executor给ApplicationManager => --num-executors = 29
每个节点的executor的数目 = 30 / 10 = 3
每个节点留4G给Hadoop/Yarn守护进程 => 每个节点executor的内存 = (64-4) / 3 = 20GB
计算堆外开销= 20GB * 7% ~ 1.4G,实际的 --executor-memory = 20 - 1.4 = 18.6GB
因此,推荐的配置如下:29 executors, 18GB memory each and 5 cores
另一种计算方式:
每个executor进程分配核数5core(保证良好HDFS吞吐量)
每台可用16 - 1 = 15core (留一个core给hadoop/yarn守护进程)
每台可用64 - 4 = 60G (留4个G给hadoop/yarn守护进程)
总核数15*10=150,总内存数60*10=600
进程数150/5=30 num executor ,减去一,实际29executor(留一个executor给ApplicationManager)
实际内存数600/29 = 20G,减去堆外开销,20*93%=18.6G
因此,推荐的配置如下:29 executors, 18GB memory each and 5 cores