[spark性能调优]spark submit资源参数调优及amazon集群示例

目录

一、spark作业基本运行原理

二、资源参数调优

Spark内存管理:

三、amazon集群资源参数示例


一、spark作业基本运行原理

资源申请与分配:

       我们使用spark-submit提交一个spark作业后,这个作业会启动一个对应的Driver进程。根据使用的部署模式(deploy-mode)不同,Driver进程可能在本地启动(client mode),也可能在集群中某个工作节点启动(cluster mode)。Driver进程本身根据我们设置的参数,占有一定数量的内存和CPU core。而Driver进程要做的第一件事情,就是向集群管理器YARN申请运行spark作业需要使用的资源,这里的资源指的就是executor进程。YARN集群管理器会根据我们为spark作业设置的资源参数,在各个工作节点上,启动一定数量的executor进程,每个executor进程都占有一定数量的内存和CPU core。

Task分配:

       在申请到作业执行所需的资源后,Driver进程就会开始调度和执行我们编写的作业代码了。Driver进程会将我们编写的spark作业代码分拆为多个stage,每个stage执行一部分代码片段,并为每个stage创建一批task,然后将这些task分配到各个executor进程中执行。Task是最小的计算单元,负责执行一模一样的计算逻辑(也就是我们自己编写的某个代码片段),只是每个task处理的数据不同而已。一个stage的所有task都执行完毕之后,会在各个节点本地的磁盘文件中写入计算中间结果,然后driver就会调度运行下一个stage。下一个stage的输入数据就是上一个stage输出的中间结果。如此循环往复,直到将我们自己编写的代码逻辑全部执行完,并且计算完所有的数据,得到我们想要的结果为止。

       Spark是根据shuffle类算子来进行stage的划分。如果我们的代码中执行了某个shuffle类算子(比如reduceByKey、join等),那么就会在该算子处,划分出一个stage界限来。可以大致理解为,shuffle算子执行之前的代码会被划分为一个stage,shuffle算子执行以及之后的代码会被划分为下一个stage。因此一个stage刚开始执行时,每个task会从上一个stage的task所在的节点,去通过网络传输拉取需要自己处理的所有key,然后对拉取到的所有相同key使用我们自己编写的算子函数执行聚合操作(比如reduceBykey算子接收的函数)。这个过程就是shuffle。

Executor内存和CPU core

       当我们在代码中执行了cache/persist等持久化操作时,根据我们选择的持久化级别的不同,每个task计算出的数据也会保存到executor进程的内存或所在节点的磁盘文件中。

       Executor的内存主要分为三块:第一块是让task执行我们自己编写的代码时使用,默认是占executor总内存的20%;第二块是让task通过shuffle过程拉取上一个stage的task输出后,进行聚合等操作时使用,默认占executor总内存的20%;第三块是数据持久化时使用的,默认占executor总内存的60%。

      Task的执行速度是跟每个executor进程的CPU core数量有直接关系的。一个cup core 同一时间只能执行一个线程。而每个executor进程上分配到多个task,都是以每个task一条线程的方式,多线程并发运行的。如果CPUcore数量比较充足,而且分配到的task数量比较合理,那么通常来说,可以比较快速和高效地执行完这些task线程。

二、资源参数调优

Driver-memory

  • 参数说明:改参数用于设置Driver进程的内存。
  • 参数调优建议:Driver的内存通常来说设置为1G。Driver负责任务的调度,和executor、AM之间的消息通信,当任务数变多,任务平行度增大时,Driver内存需要相应增大,否则会由于内存不足,导致进程之间通信断裂问题。还需要注意的是,如果需要使用collect算子将数据全部拉取到Driver上进行处理,那么必须确保Driver的内存足够大,否则会出现OOM内存溢出的问题。

Num-executors

  • 参数说明:该参数用于设置集群的各个工作节点上启动executor的数量。
  • 参数调优建议:每个spark作业的运行一般设置50~100个左右的executor进程比较合适。设置的太少,无法充分利用集群资源;设置的太多,大部分队列可能无法给予充分的资源。

Executor-cores

  • 参数说明:该参数用于设置每个executor进程的CPU core数量。这个参数决定了每个executor并行执行task线程的能力。因为每个CPU core只能执行一个task线程,因此每个executor进程的CPU core数量越多,越能够快速的执行完分配给自己的所有task线程。
  • 参数调优建议:executor的CPU core数量设置为2~4个最为合适。同样得根据资源队列来定,可以看看资源队列的最大CPU core限制是多少,再依据设置的executor数量,来决定每个executor可以分配到几个CPU core。同样建议,如果跟其他人共享这个队列,那么num-executors * executor-cores 不要超过队列总CPU core的1/3~1/2比较合适,避免影响其他同学的作业运行。

Executor-memory

  • 参数说明:该参数用于设置每个executor进程的内存。Executor内存的大小,很多时候直接决定了spark作业的性能,而且跟常见的JVM OOM异常,也有直接的关联。
  • 参数调优建议:每个executor进程的内存设置为4G~8G较为合适,但这只是一个参考值,具体设置还是根据资源队列来定。Num-executors * executor-memory,是不能超过队列的最大内存量。

Spark.default.parallelism

  • 参数说明:该参数用于设置每个stage的默认task数量。
    • 参数调优建议:spark作业的默认task数量为500~1000个较为合适。如果这个参数不设置,那么此时就会导致spark根据底层HDFS的block数量来设置task数量,默认一个HDFS block对应一个task。通常来说,spark默认设置的数量是偏少的,如果task数量偏少的话,就会导致前面设置好的executor参数都前功尽弃。试想一下,无论executor进程有多少个,内存和CPU有多大,但是task只有1个或者10个,90%的executor进程可能根本就没有task执行,也就是白白浪费资源。Spark官网建议的原则是,设置该参数为num-executors*executor-core的2~3倍较为合适,此时可以充分利用spark集群资源

Spark.storage.memoryFraction(Before spark 1.6)

  • 参数说明:该参数用于设置持久化数据在executor内存中能占的比例,默认为0.6。根据选择的不同的持久化策略,如果内存不够时,可能数据就不会持久化,或者数据写入硬盘。
  • 参数调优建议:如果spark作业中,有较多的持久化操作,该参数的值可以适当提高一些,保证持久化的数据能够容纳在内存中。必满内存不够缓存所有数据,导致数据只能写入磁盘中,降低了性能。此外,如果发现作业由于频繁的gc导致运行缓慢,意味着task执行用户代码的内存不足,同样建议调低这个参数值。

Spark.shuffle.memoryFraction(Before spark 1.6)

  • 参数说明:该参数用于设置shuffle过程中一个task拉取到上个task的输出后,进行聚合操作时能够使用的executor内存比例,默认是0.2。Shuffle操作在进行聚合时,如果发现使用的内存超出了20%的限制,那么多余的数据就会溢写到磁盘文件中,极大地降低性能。
  • 参数调优建议:如果shuffle操作较多,建议提高shuffle操作的内存占比,避免shuffle过程中数据过多时内存不够用,溢写到磁盘,降低性能。此外,如果发现作业由于频繁的gc导致运行缓慢,意味着task执行用户代码的内存不够用,同样建议调低这个参数值。

spark.memory.fraction (After spark 1.6)

  • 参数说明:该参数用于设置execution和storage数据在executor内存中能占的比例,默认0.75。
  • 参数调优建议:该参数设置过小,内存不够用,会导致数据溢写到磁盘,降低性能。同时发现作业由于频繁的gc导致运行缓慢,意味着task执行用户代码的内存不够用,建议调低该参数。

Spark.memory.storageFraction(After spark 1.6)

  • 参数说明:该参数用于设置spark.memory.fraction中storage内存占比,默认0.5。
  • 参数调优建议:在spark1.6之后的版本中,storage memory和execution memory可以互借,如果storage memory的空间已经超过了设定的大小,execution向storage借用内存时,storage memory只能释放出超过设定的空间给execution。

Spark内存管理:

Before 1.6

      对于一个executor,内存由以下三个部分组成:(spark1.6之后的版本,需要设置spark.memory.useLegacyMode=true,下面参数配置才起作用)

      Execution memory:这片区域是为了解决shuffles,joins,sorts and aggregations过程中为了避免频繁IO需要的buffer。通过spark.shuffle.memoryFraction配置,默认0.2。

     Storage memory:这边区域是为了解决block cache(cache\persist方法),broadcast,以及task results的存储。可以通过spark.storage.memoryFraction设置,默认0.6。

     Other memory:给系统预留,因为程序本身运行也是需要内存的,默认0.2。

     这种内存分配机制最大的问题是,每块内存的使用都不能超过各自的上限,即使另外一块内存空闲,仍不可被其他部分使用

After 1.6

Executor 中的内存分为两部分:

       User memory:给系统预留,因为程序本身运行也是需要内存的,默认0.25。

   Spark memory:这部分内存包含storage memory 和 execution memory两部分,这两部分边界由spark.memory.storageFraction参数设置,默认为0.5,新内存管理模型中的优点是,这个边界不是固定的,如果一个区域内存不够用时,可以从另一区域借用。Execution memory保存的是用来计算的中间结果,如果计算过程中找不到存储的数据,会导致任务失败,因而这部分内存的blocks不会被其他线程的task挤出去。Storage memory 缓存内存中数据,如果数据被驱逐,要想再拿到这些数据时,重新计算就可以。Execution向storage借用空间时,有两种可能:一种,storage memory有空闲空间,可以直接增大execution大小,减小storage memory大小;另一种,storage memory空间已经超过设定的大小,强制将storage memory释放超过设定值的部分空间,还给execution memory。反之亦然,只是execution memory 占用的空间不可释放,借给storage memory使用。

三、amazon集群资源参数示例

以m4.xlarge实例为例,首先查询各个工作节点最大可使用的CPU和内存资源,/etc/hadoop/conf/yarn-site.xml

  <property>
    <name>yarn.nodemanager.resource.cpu-vcores</name>
    <value>4</value>
  </property>
  <property>
    <name>yarn.nodemanager.resource.memory-mb</name>
    <value>12288</value>
  </property>

资源分配满足最大资源限制:

Executor-num * executor-cores <= 4

Executor-num * executor-memory <= 12g

根据作业具体情况设置资源参数如下:

--driver-memory 2G      //driver memory 设置太小,会出现OOM问题,导致任务失败
--executor-memory 10G 
--executor-cores 4  
--num-executors 1
--conf spark.default.parallelism=200  // nodes-num * executor-num * executor-cores * (2/3)
--conf spark.memory.fraction=0.6   //根据GC 时间调整
--conf spark.memory.storageFraction=0.5   //设定的内存值:heap-reserved memory * 0.6 * 0.5

参考资料:

 

https://tech.meituan.com/spark_tuning_basic.html

https://blog.csdn.net/Full_Stack_delp/article/details/72878236

https://www.cnblogs.com/dreamfly2016/p/5720526.html

https://amazonaws-china.com/cn/blogs/big-data/submitting-user-applications-with-spark-submit/

猜你喜欢

转载自blog.csdn.net/woniu201411/article/details/81985666