Spark 内核调度

Spark 内核调度

1.1 DAG(有向无环图)

Spark的核心是根据RDD来实现的,Spark Scheduler则为Spark核心实现的重要一环,其作用就是任务调度。Spark的任务调度就是如何组织任务去处理RDD中每个分区的数据,根据RDD的依赖关系构建DAG,基于DAG划分Stage,将每个Stage中的任务发到指定节点运行。基于Spark的任务调度原理,可以合理规划资源利用,做到尽可能用最少的资源高效地完成任务计算。

以下为词频统计WordCount的DAG图:

image-20230406200047794

Job 和 Action

tmpB19C

最后面的Action算子带了各自的链条,每个产生一个Job,同时每个Job有各种的DAG图。如图便产生3个DAG。

1个Action = 1个DAG = 1个Job

DAG 和 分区

DAG是Spark代码的逻辑执行图,这个DAG的最终作用是:构建物理上的Spark详细执行计划
所以,由于Spak是分布式(多分区)的,那么DAG和分区之间也是有关联的。

image-20230406203215108

如图便是带有分区关系的DAG图

1.2 DAG的宽窄依赖和阶段划分

SparkRDD前后之间的关系分为窄依赖和宽依赖

窄依赖:父RDD的一个分区,全部将数据发给子RDD的一个分区
宽依赖:父RDD的一个分区,将数据发给子RDD的多个分区
宽依赖还有一个别名:shuffle

窄依赖

父RDD的一个分区,全部将数据发给子RDD的一个分区

image-20230407154701493

宽依赖

父RDD的一个分区,将数据发给子RDD的多个分区

image-20230407155049981

阶段划分

对于Spark来说,会根据DAG,按照宽依赖,划分不同的DAG阶段
划分依据:从后向前,遇到宽依赖就划分出一个阶段。称之为stage

image-20230407155242790

如图,可以看到,在DAG中,基于宽依赖。将DAG划分成了2个stage
在stage的内部,一定都是:窄依赖

1.3 内存迭代运算

image-20230407160300574

如图,基于带有分区的DAG以及阶段划分。可以从图中得到逻辑上最优的task分配:一个task是一个线程来具体执行。
那么如上图,task1中rdd1 rdd2 rdd3的迭代计算,都是由一个task线程完成,这一阶段的这一条线,是纯内存计算。
如上图,task1 task2 task3,就形成了三个并行的内存计算管道。

Spark默认受到全局并行度的限制,除了个别算子有特殊分区情况,大部分的算子,都会遵循全局并行度的要求,来规划自己的分区数
如果全局并行度是3,其实大部分算子分区都是3

注意:Spark我们一般推荐只设置全局并行度,不要在算子上设置并行度。除了一些排序算子外,计算算子就让他们默认开分区就可以了。

问答

一、Spark是怎么做内存计算的?DAG的作用?Stage阶段划分的作用?

  1. Spark会产生DAG图

  2. DAG图会基于分区和宽窄依赖关系划分阶段

  3. 一个阶段的内部都是窄依赖,窄依赖内,如果形成前后1:1的分区对应关系,就可以产生许多内存迭代计算的管道

  4. 这些内存迭代计算的管道,就是一个个具体的执行Task

  5. 一个Task是一个具体的线程,任务跑在一个线程内,就是走内存计算了。

二、Spark为什么比MapReduce快?

  1. Spark的算子丰富,MapReduce算子匮乏(Map和Reduce),MapReduce这个编程模型,很难在一套MR中处理复杂的任务。很多的复杂任务,是需要写多个MapReduce进行串联。多个MR串联通过磁盘交互数据。
  2. Spark可以执行内存迭代,算子之间形成DAG基于依赖划分阶段后,在阶段内形成内存迭代管道。而MapReduce的Map和Reduce之间的交互依旧是通过硬盘来交互的。

1.4 Spark 并行度

Spark的并行:在同一时间内有多少个task在同时跑

并行度:并行能力的设置
比如设置并行度6,其实就是要6个task并行在跑,在有了6个task并行的前提下,rdd的分区就被规划成6个分区了。

如何设置并行度

可以在代码中和配置文件中以及提交程序的客户端参数中设置
优先级从高到低:

  1. 代码中
  2. 客户端提交参数中
  3. 配置文件中
  4. 默认(1,但是不会全部以1来跑,多数时候基于读取文件的分片数量来作为默认并行度)

全局并行度配置的参数:
s p a r k . d e f a u l t . p a r a l l e l i s m \textcolor{CornflowerBlue}{spark.default.parallelism} spark.default.parallelism

全局并行度-推荐

配置文件中:

conf/spark-defaults.conf中设置
spark.default.parallelism 100

在客户端提交参数中:

bin/spark-submit --conf "spark.default.parallelism=100"

在代码中设置:

conf = SaprkConf()
conf.set("spark.default.parallelism", "100")

针对RDD的并行度设置-不推荐

只能在代码中写如下算子:

  • repartition算子
  • coalesce算子
  • partitionBy算子

集群中如何规划并行度

结论:设置为CPU总核心的2~10倍
比如集群可用CPU核心是100个,我们建议并行度是200~1000
确保是CPU核心的整数倍即可,最小是2倍,最大一般10倍或更高(适量)均可

为什么要设置最少2倍?

CPU的一个核心同一时间只能干一件事情
所以,在100个核心的情况下,设置100个并行,就能让CPU100%出力
这种设置下,如果task的压力不均衡,某个task先执行完了。就导致某个CPU核心空闲
所以,我们将Task(并行)分配的数量变多,比如800个并行,同一时间只有100个在运行,700个在等待
但是可以确保,某个task运行完了。后续有task补上,不让CPU闲下来,最大程度利用集群的资源
故规划并行度,只看集群总CPU核数

1.5 Spark 的任务调度

Spark的任务,由Driver进行调度,这个工作包含:

  1. 逻辑DAG产生
  2. 分区DAG产生
  3. Task划分
  4. 将Task分配给Executor并监控其工作

image-20230409153325338

如图,Spark程序的调度流程如图:

  1. Driver被构建出来
  2. 构建SparkContext(执行环境入口对象)
  3. 基于DAG Scheduler(DAG调度器)构建逻辑Task分配
  4. 基于TaskScheduler(Task调度器)将逻辑Task分配到各个Executor上干活,并监控它们
  5. Worker(Executor),被TaskScheduler管理监控,听从它们的指令干活,并定期汇报进度

其中,前4步都是在Driver上完成的,第5步是在Worker上完成的。

Driver内的两个组件

DAG调度器

工作内容:将逻辑的DAG图进行处理,最终得到逻辑上的Task划分,不考虑有多少个executor

Task调度器

工作内容:基于DAG Scheduler的产出,来规划这些逻辑的task,应该在哪些物理的executor上运行,以及监控管理它们的运行

一般把executor的数量设置为机器的数量,以减少网络io的传输

1.6 拓展-Spark运行中的概念名词大全

image-20230409155843832

层级关系梳理:

  1. 一个Spark环境可以运行多个Application
  2. 一个代码运行起来,会成为一个Application
  3. Application内部可以有多个Job
  4. 每个Job由一个Action产生,并且每个Job有自己的DAG执行图
  5. 一个Job的DAG图会基于宽窄依赖划分成不同的阶段
  6. 不同阶段内基于分区数量,形成多个并行的内存迭代管道
  7. 每一个内存迭代管道形成一个Task(DAG调度器划分将Job内划分出具体的task任务,一个Job被划分出来的task在逻辑上称之为这个job的taskset)
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值