一、任务流程

我们以生产中常用的yarn环境为例

  • 提交任务到yarn
  • yarn申请分配资源
  • 根据RDD流程构建DAG(有向无环图)
  • DAGsheduler将dag划分及分解stage
  • 根据分解的stage生成不同的task
  • 将task提交到不同的Executor上执行
  • 执行完毕,释放资源

二、线程模型

Spark任务的线程模型特点:

  • 每个节点上可以运行一个或多个Executor服务。每个应用程序在一个工作者节点上只会有一个Executor。多个应用程序则会有多个Executor
  • 每个Executor配有一定数量的slot,表示该Executor中可以同时运行多少个ShuffleMapTask或者ReduceTask;
  • 每个Executor单独运行在一个JVM进程中,每个Task则是运行在Executor中的一个线程;
  • 同一个Executor内部的Task可共享内存,比如通过函数SparkContext.broadcast广播的数据如文件或者数据结构只会在每个Executor中加载一次,而不会像MapReduce那样,每个Task加载一次
  • Executor一旦启动后,将一直运行,且它的资源可以一直被Task复用,直到Spark程序运行完成后才释放退出。
  • 总体上看,Spark采用的是经典的scheduler/workers模式,每个Spark应用程序运行的第一步是构建一个可重用的资源池,然后 在这个资源池里运行所有的ShuffleMapTask和ReduceTask(注意,尽管Spark编程方式十分灵活,不再局限于编写Mapper和 Reducer,但是在Spark引擎内部只用两类Task便可表示出一个复杂的应用程序,即ShuffleMapTask和ResultTask),而 MapReduce应用程序则不同,它不会构建一个可重用的资源池,而是让每个Task动态申请资源,且运行完后马上释放资源

三、shuffle

大多数spark作业的性能主要就是消耗了shuffle过程,shuffle时数据会重新分片并写磁盘,该环节包含了大量的磁盘IO、序列化、网络数据传输等操作。因此,如果要让作业的性能更上一层楼,就有必要对shuffle过程进行调优。但是也必须提醒大家的是,影响一个Spark作业性能的因素,主要还是代码开发、资源参数以及数据倾斜,shuffle调优只能在整个Spark的性能调优中占到一小部分。

四、宽依赖与窄依赖

如下图,一个父rdd数据有可能进入多个rdd的操作都会产生宽依赖,相反一个父rdd数据只进入一个子rdd称为窄依赖。

注意,union 操作如果先分组后union,且没有改变分区器,此时union不会产生宽依赖。

作图spark中划分stage 简述如何在spark中划分stage_作图spark中划分stage

五、stage的划分

产生宽依赖时,数据会产生shuffle,shuffle后的操作依赖于shuffle前的操作结果全部完毕才能开始。

在DAG调度的过程中,Stage阶段的划分是根据是否有shuffle过程,也就是存在ShuffleDependency宽依赖的时候,需要进行shuffle,这时候会将作业job划分成多个Stage;

spark 划分 stage 的整体思路是:从后往前推,遇到宽依赖就断开,划分为一个 stage;遇到窄依赖就将这个 RDD 加入该 stage 中。如下图:

作图spark中划分stage 简述如何在spark中划分stage_作图spark中划分stage_02

六、stage生成Task 的类型

在 spark 中,Task 的类型分为 2 种:ShuffleMapTask 和 ResultTask
DAG 的最后一个阶段会为每个结果的 partition 生成一个 ResultTask,即每个 Stage里面的 Task 的数量是由该 Stage 中最后一个 RDD 的 Partition 的数量所决定的。而其余所有阶段都会生成 ShuffleMapTask;之所以称之为 ShuffleMapTask 是因为它需要将自己的计算结果通过 shuffle 到下一个 stage 中;

七、spark shuffle四种策略:

负责shuffle过程的执行、计算和处理的组件主要就是ShuffleManager,也即shuffle管理器。
ShuffleManager随着Spark的发展有两种实现的方式,分别为HashShuffleManager(spark1.2之前使用)和SortShuffleManager,因此spark的Shuffle有Hash Shuffle和Sort Shuffle两种

在Spark 1.2以前,默认的shuffle计算引擎是HashShuffleManager。该ShuffleManager而HashShuffleManager有着一个非常严重的弊端,就是会产生大量的中间磁盘文件,进而由大量的磁盘IO操作影响了性能。因此在Spark 1.2以后的版本中,默认的ShuffleManager改成了SortShuffleManager。SortShuffleManager相较于HashShuffleManager来说,有了一定的改进。主要就在于,每个Task在进行shuffle操作时,虽然也会产生较多的临时磁盘文件,但是最后会将所有的临时文件合并(merge)成一个磁盘文件,因此每个Task就只有一个磁盘文件。在下一个stage的shuffle read task拉取自己的数据时,只要根据索引读取每个磁盘文件中的部分数据即可。
总体来说就是:

spark1.2版本以前:hashShuffleManager
	未经优化的hashShuffleManager(会产生大量小文件)
	经过优化的hashShuffleManager(后续task可以复用之前的文件)
spark1.2版本以后:SortShuffleManager
	普通机制(将小文件合并为大一个文件,写磁盘前会对数据进行key排序)
	ByPass机制(将小文件合并为大一个文件,不会排序)

八、spark 的shuffle调优:

主要是调整缓冲的大小,拉取次数重试重试次数与等待时间,内存比例分配,是否进行排序操作等等

  • spark.shuffle.file.buffer
    参数说明:该参数用于设置shuffle write task的BufferedOutputStream的buffer缓冲大小(默认是32K)。将数据写到磁盘文件之前,会先写入buffer缓冲中,待缓冲写满之后,才会溢写到磁盘。
    调优建议:如果作业可用的内存资源较为充足的话,可以适当增加这个参数的大小(比如64k),从而减少shuffle write过程中溢写磁盘文件的次数,也就可以减少磁盘IO次数,进而提升性能。在实践中发现,合理调节该参数,性能会有1%~5%的提升。
  • spark.reducer.maxSizeInFlight:
    参数说明:该参数用于设置shuffle read task的buffer缓冲大小,而这个buffer缓冲决定了每次能够拉取多少数据。
    调优建议:如果作业可用的内存资源较为充足的话,可以适当增加这个参数的大小(比如96m),从而减少拉取数据的次数,也就可以减少网络传输的次数,进而提升性能。在实践中发现,合理调节该参数,性能会有1%~5%的提升。
  • spark.shuffle.io.maxRetries and spark.shuffle.io.retryWait:
    spark.shuffle.io.retryWait:huffle read task从shuffle write task所在节点拉取属于自己的数据时,如果因为网络异常导致拉取失败,是会自动进行重试的。该参数就代表了可以重试的最大次数。(默认是3次)
    spark.shuffle.io.retryWait:该参数代表了每次重试拉取数据的等待间隔。(默认为5s)
    调优建议:一般的调优都是将重试次数调高,不调整时间间隔。
  • spark.shuffle.memoryFraction:
    参数说明:该参数代表了Executor内存中,分配给shuffle read task进行聚合操作的内存比例。
  • spark.shuffle.manager
    参数说明:该参数用于设置shufflemanager的类型(默认为sort)。Spark1.5x以后有三个可选项:
Hash:spark1.x版本的默认值,HashShuffleManager
Sort:spark2.x版本的默认值,普通机制,当shuffle read task 的数量小于等于spark.shuffle.sort.bypassMergeThreshold参数,自动开启bypass 机制
tungsten-sort:
  • spark.shuffle.sort.bypassMergeThreshold
    参数说明:当ShuffleManager为SortShuffleManager时,如果shuffle read task的数量小于这个阈值(默认是200),则shuffle write过程中不会进行排序操作。
    调优建议:当你使用SortShuffleManager时,如果的确不需要排序操作,那么建议将这个参数调大一些
  • spark.shuffle.consolidateFiles:
    参数说明:如果使用HashShuffleManager,该参数有效。如果设置为true,那么就会开启consolidate机制,也就是开启优化后的HashShuffleManager。
    调优建议:如果的确不需要SortShuffleManager的排序机制,那么除了使用bypass机制,还可以尝试将spark.shffle.manager参数手动指定为hash,使用HashShuffleManager,同时开启consolidate机制。在实践中尝试过,发现其性能比开启了bypass机制的SortShuffleManager要高出10%~30%。