返回顶部

我的蜡笔丢了

  博客园  :: 首页  :: 新随笔  ::  :: 订阅 订阅  :: 管理

资源影响因素

Spark和Yarn管理的资源限制因素:

  • CPU
  • 内存
  • 磁盘
  • 网络I/O

Spark和Yarn管理的两个主要资源为CPU和内存,剩下不会主动管理,所以设置资源也是主要通过这两方面进行设置。

资源优化配置

  在资源配置中,可以用过代码的设置sparkconf进行设置,或者在脚本里通过参数进行设置,脚本的优先级大于代码的优先级。YARN的相关属性是

# 控制每个节点上的容器使用的最大内存总和。
yarn.nodemanager.resource.memory-mb
# 控制每个节点上的容器使用的最大内核总数。
yarn.nodemanager.resource.cpu-vcores 

  YARN请求的内存时,需要注意一下两点:

  • --executor-memory/spark.executor.memory 控制执行程序堆的大小,但是JVM也可以在堆外使用一些内存,例如,spark.yarn.executor.memoryOverhead 用于内联的String和直接字节缓冲区。属性的值将添加到执行程序内存中,以确定每个执行程序对YARN的完整内存请求。默认为max(384,.07 * spark.executor.memory)。 
  •  YARN可能会将请求的内存向上舍入一点。YARN 和属性分别控制最小和增量请求值。yarn.scheduler.minimum-allocation-mbyarn.scheduler.increment-allocation-mb

在确定Spark执行程序的大小时要注意的最后几个问题:

  • 应用程序主服务器是一个非执行程序容器,具有从YARN请求容器的特殊功能,它占用必须自己预算的资源。在yarn-client模式下,它默认为1024MB和一个vcore。yarn-cluster模式下,应用程序主控程序运行驱动程序,因此使用属性支持其资源通常很有用--driver-memory--driver-cores
  • 运行内存过多的执行程序通常会导致过多的垃圾回收延迟(当内存不足时会触发JVM回收内存)。对于单个执行器来说,64GB是一个不错的上限。
  • executor遇到大量并发线程效率会降低。官方的建议是,每个executor最多可以设置五个task,以实现较高效率的写入吞吐量,因此最好将每个executor的vcores保持在该数量以下。
  • 启动太多executor执行比较少任务的时候,会抛弃同个executor起多线程的效率,反而降低效率。例如,广播变量需要在每个executor上复制一次,因此太多executor将产生更多的副本成本。

脚本模板

./spark-submit \
--class 入口类
--master yarn-cluster \
--num-executors 100 \
--executor-memory 6G \
--executor-cores 4 \
--driver-memory 1G \
--conf spark.default.parallelism=1000 \
--conf spark.storage.memoryFraction=0.5 \
--conf spark.shuffle.memoryFraction=0.3 \
jar包路径 参数

num-executors

  参数说明:该参数用于设置Spark作业总共要用多少个Executor进程来执行。Driver在向YARN集群管理器申请资源时,YARN集群管理器会尽可能按照你的设置来在集群的各个工作节点上,启动相应数量的Executor进程。不设置默认值是2。

  参数调优建议:根据从节点的数据量设置对应的数量,设置太多会让一台节点起多个executor,影响机器的性能,起太少会让节点空闲。

executor-memory

  参数说明:该参数用于设置每个Executor进程的内存。Executor内存的大小,很多时候直接决定了Spark作业的性能,而且跟常见的JVM OOM异常,也有直接的关联。

  参数调优建议:具体情况得根据不同部门的资源队列和程序部署的情况来决定,数据源大小也是一个参考点;但num-executors * executor-memory,是不能超过队列的最大内存量的。此外,如果资源是和别人共享的情况或者多程序的情况,那么申请的内存量最好不要超过资源队列最大总内存的1/3~1/2,避免你自己的Spark作业占用了队列所有的资源,导致别的同事的作业无法运行。

 executor-cores

  参数说明:该参数用于设置每个Executor进程的CPU core数量。这个参数决定了每个Executor进程并行执行task线程的能力。因为每个CPU core同一时间只能执行一个task线程,因此每个Executor进程的CPU core数量越多,越能够快速地执行完分配给自己的所有task线程。

  参数调优建议:每个Executor同时执行5个task时HDFS的写的吞吐量是最好的。所以,建议将executor-cores的值为3-5。这个限制也要根据资源情况来限定,例如资源队列里最大的CPU core数量,假设你的executor的内存没办法配置太大,那么executor-cores不能配置太多,不然会导致内存不够而引起内存溢出。

driver-memory

  参数说明:该参数用于设置Driver进程的内存。

  参数调优建议:Driver的内存通常来说不设置,或者设置1G左右应该就够了。但你的程序有使用collect算子将RDD的数据全部拉取到Driver上进行处理(或者是用map side join操作),那么必须确保Driver的内存足够大,否则会出现OOM内存溢出的问题。

spark.default.parallelism

  参数说明:该参数用于设置每个stage的默认task数量,也可以认为是分区数。

  参数调优建议:Spark官网的建议是,设置该参数为num-executors * executor-cores的2~3倍较为合适;如果不设置这个参数,那么会导致Spark自己根据底层HDFS的block数量来设置task的数量,默认是一个HDFS block对应一个task。通常来说,Spark默认设置的数量是偏少的(比如就几十个task),如果task数量偏少的话,就会导致你前面设置好的Executor的cpu闲置,浪费资源。

spark.storage.memoryFraction

  参数说明:该参数用于设置RDD持久化数据在Executor内存中能占的比例,默认是0.6。也就是说,默认Executor 60%的内存,可以用来保存持久化的RDD数据。根据你选择的不同的持久化策略,如果内存不够时,可能数据就不会持久化,或者数据会写入磁盘。

  参数调优建议:如果Spark作业中,有较多的RDD持久化操作,该参数的值可以适当提高一些,保证持久化的数据能够容纳在内存中。避免内存不够缓存所有的数据,导致数据只能写入磁盘中,降低了性能。但是如果Spark作业中的shuffle类操作比较多,而持久化操作比较少,那么这个参数的值适当降低一些比较合适。此外,如果发现作业由于频繁的gc导致运行缓慢(通过spark web ui可以观察到作业的gc耗时),意味着task执行用户代码的内存不够用,那么同样建议调低这个参数的值。

 spark.shuffle.memoryFraction

  参数说明:该参数用于设置shuffle过程中一个task拉取到上个stage的task的输出后,进行聚合操作时能够使用的Executor内存的比例,默认是0.2。也就是说,Executor默认只有20%的内存用来进行该操作。shuffle操作在进行聚合时,如果发现使用的内存超出了这个20%的限制,那么多余的数据就会溢写到磁盘文件中去,此时就会极大地降低性能。

  参数调优建议:如果Spark作业中的RDD持久化操作较少,shuffle操作较多时,建议降低持久化操作的内存占比,提高shuffle操作的内存占比比例,避免shuffle过程中数据过多时内存不够用,必须溢写到磁盘上,降低了性能。此外,如果发现作业由于频繁的gc导致运行缓慢,意味着task执行用户代码的内存不够用,那么同样建议调低这个参数的值。

配置参数

假设集群服务器配置:

节点:10
核数:16/台
内存:64GB/台

配置一:使用较小的executors

  这里起和核数相同的executor

--num-executors =  16 x 10 = 160
--executor-cores = 1 (one executor per core)
--executor-memory = `mem-per-node/num-executors-per-node`
                     = 64GB/16 = 4GB

  由于每个executor只分配了一个核,我们将无法利用在同一个JVM中运行多个任务的优点。 此外,共享/缓存变量(如广播变量和累加器)将在节点的每个核心中复制16次。 最严重的就是,我们没有为Hadoop / Yarn守护程序进程留下足够的内存开销,我们还忘记了将ApplicationManagers运行所需要的开销加入计算。

配置二:使用较大的executors

  每个executor都有16个核

--num-executors =  10
--executor-cores = 16 
--executor-memory = `mem-per-node/num-executors-per-node`
                     = 64GB/1 = 64GB

  由于HDFS客户端遇到大量并发线程会出现一些bug,即HDFS吞吐量会受到影响。同时过大的内存分配也会导致过多的GC延迟。

配置三:使用优化的executors

--num-executors =  29
--executor-cores = 5 
--executor-memory = 19GB

(1)给每个Executor分配5个core即executor-cores=5,这样对HDFS的吞吐量会比较友好。
(2)为后台进程留一个core,则每个节点可用的core数是16 - 1 = 15。所以集群总的可用core数是15 x 10 = 150
(3)每个节点上的Executor数就是 15 / 5 = 3,集群总的可用的Executor数就是 3 * 10 = 30 。为ApplicationManager留一个Executor,则num-executors=29
(4)每个节点上每个Executor可分配的内存是 (64GB-1GB) / 3 = 21GB(减去的1GB是留给后台程序用),除去MemoryOverHead=max(384MB, 7% * 21GB)=2GB,所以executor-memory=21GB - 2GB = 19GB
 此方法既保证了在一个JVM实例里能同时执行task的优势,也保证了hdfs的吞吐量。

配置四:在配置三基础上再优化

  按照方法三,每个Executor分配到的内存是19GB,假设10GB内存就够用了。那么此时我们可以将executor-cores降低如降低为3,那么每个节点就可以有15 / 3 = 5个Executor,那么总共可以获得的Executor数就是 (5 * 10) - 1 =49,每个节点上每个Executor可分配的内存是(64GB-1GB) / 5 = 12GB,除去
MemoryOverHead=max(384MB, 7% * 12GB)=1GB,所以executor-memory=12GB - 1GB = 11GB
所以最后的参数配置是

--num-executors =  49
--executor-cores = 3 
--executor-memory = 11GB

  这里只是做一个参考,具体的实际情况依照个人的资源情况,参考有数据量大小,部门的资源使用情况等。一切以实际为准,这里只是搬运整理下资源以便翻阅。

参考博客:https://www.cnblogs.com/arachis/p/spark_parameters.html  http://www.dzjqx.cn/news/show-28638.html https://blog.cloudera.com/how-to-tune-your-apache-spark-jobs-part-2/

posted on 2020-08-15 11:30  我的蜡笔丢了  阅读(5278)  评论(0编辑  收藏  举报