spark-原理总结

一、总体框架结构图 

 由上图我们可以看到Spark应用程序架构主要由Driver Program和Executor构成,Driver负责运行main()和创建SparkContext,Executor主要负责执行Task任务。

二、各大重要组件和概念

2.1 重要概念

概念功能
Application用户定义的Spark应用程序,用户提交后,Spark会为应用分配资源,调度和执行相关任务
Job- 一个App中可以包含多个Job,每个Job都是由一个RDD Graph转化而来的
- 由Action算子触发
Stage- 每个Job会根据RDD之间的宽依赖(Shuffle Dependency)来划分成多个Stage,每一个Stage中包含一组Task(也就是TaskSet)
- 每个Stage中的Task类型都是相同的
Task- 一个分区对应一个Task
- Task执行RDD中对应的算子,Task被封装TaskRunner后放入Executor的线程中来执行,并由TaskRunner来进行调度
- Task类型有ShuffleMapTask和ResultTask
Worker可以运行App代码的节点,如Yarn中NodeManager
DAGScheduler用来构建基于Stage的有向无环图,划分的依据是根据RDD之间的宽依赖
TaskScheduler- 将TaskSet提交给Executor去运行
- 维护TaskSet的运行状态标签,负责Task的失效重执行

2.2 相关组件

组件功能
Driver- 运行Application的main函数并创建SparkContext
- SparkContext的作用就是与Cluster Manager通讯,进行资源申请、任务的调度等。
Executor- 某个App运行在Worker上的一个进程,用于执行App的Task
- 每个App都有独立的Executor,Executor上运行的Task取决于分配的core数
Cluster Manager资源调度服务,用于资源的分配和调度,有standalone、mesos和yarn三种模式

三、应用的提交执行流程

3.1 运行模式

1)Spark应用的运行模式有以下几种:

  • local[n]:本地模式
  • standalone:使用spark内部的资源管理器,master-slaves架构
  • mesos : Apache下的一个资源管理框架
  • yarn : Hadoop下的一个资源管理框架

2)根据Driver的运行位置来划分有以下两种运行模式:

  • Cluster:Driver和Executor运行在集群中的Worker上
  • Client:Driver运行在客户端上,Executor运行在Worker上

3)命令行

  • spark-shell:bin/spark-shell --master yarn --deploy-mode client(yarn的client模式)
  • spark-submit:spark-shell底层也是调用spark-submit,如果你想知道它有什么参数,可以直接在spark目录下输入 spark-submit来查看

3.2 流程

这里我们就以 Spark On Yarn 为例来说明

3.2.1 Yarn-Client 模式

流程如下:

  1. 在客户端通过spark-submit向Yarn提交Application
  2. 应用在Client启动Driver,创建SparkContext并进行初始化
  3. ResourceManger为应用申请一个Container来启动Application Master,用来与Client中的SparkContext进行通讯。
  4. Application Master即承担起了为应用申请资源运行Executor的责任,一旦申请到资源,那么就会在指定的NodeManager的Container上创建Executor进程,Executor会向SparkContext注册,保持通讯
  5. SparkContext会根据任务所需资源,数据本地性等条件指定Task的运行在哪个Executor上,将Task分发到指定Executor并开始执行,Executor会周期向SparkContext汇报任务运行情况
  6. 一旦所有任务执行完毕,SparkContext会向ResourceManger申请注销自己并关闭

3.2.2 Yarn-Cluster 模式

流程如下:

  1. 在客户端通过spark-submit向Yarn提交Application
  2. ResourceManger为应用申请一个Container来启动Application Master,并且在Application Master进行SparkContext等初始化。
  3. Application Master即承担起了为应用申请资源运行Executor的责任,一旦申请到资源,那么就会在指定的NodeManager的Container上创建Executor进程,Executor会向SparkContext注册,保持通讯
  4. Applicaiton Master会根据任务所需资源,数据本地性等条件指定Task的运行在哪个Executor上,将Task分发到指定Executor并开始执行,Executor会周期向Applicaiton Master汇报任务运行情况
  5. 一旦所有任务执行完毕,Applicaiton Master会向ResourceManger申请注销自己并关闭

3.3 Job的全生命周期

  • action算子触发
  • 内部实际就是sc.runJob,最终调用了submitJob
  • submit中最终调用了DAGScheduler的submitStage,由最后一个stage往前找,直到parentStage缺失调用submitMissingTask
  • DAGScheduler将TaskSet提交给TaskScheduler执行
  • TaskScheduler中实际调用了submit方法,而该方法是使用远程调用的方式来调用CoraseGraintedExecutorBackend的launchTask方法
  • launchTask则是将包装了Task的TadkRunner丢进线程池中去运行

四、执行原理

Spark应用程序以进程集合为单位在分布式集群上运行,通过driver程序的main方法创建的SparkContext对象与集群交互。

1、Spark通过SparkContext向Cluster manager(资源管理器)申请所需执行的资源(cpu、内存等)

2、Cluster manager分配应用程序执行需要的资源,在Worker节点上创建Executor

3、SparkContext 将程序代码(jar包或者python文件)和Task任务发送给Executor执行,并收集结果给Driver。

\
图1 Spark运行原理图

 

Spark详细运行过程如下图

\
图2 Spark运行详细流程

 

\
图3 Spark运行流程描述

 


涉及的几个定义和详细的运行过程如下:

1、Application:Spark应用程序

指的是用户编写的Spark应用程序,包含了Driver功能代码和分布在集群中多个节点上运行的Executor代码。

Spark应用程序,由一个或多个作业JOB组成,如下图所示。

\
图4Spark应用程序组成

 

2、Driver:驱动程序

Spark中的Driver即运行上述Application的Main()函数并且创建SparkContext,其中创建SparkContext的目的是为了准备Spark应用程序的运行环境。在Spark中由SparkContext负责和ClusterManager通信,进行资源的申请、任务的分配和监控等;当Executor部分运行完毕后,Driver负责将SparkContext关闭。通常SparkContext代表Driver,如下图所示。

\
图5 Driver驱动程序组成

 

3、Cluster Manager:资源管理器

指的是在集群上获取资源的外部服务,常用的有:Standalone,Spark原生的资源管理器,由Master负责资源的分配;Haddop Yarn,由Yarn中的ResearchManager负责资源的分配;Messos,由Messos中的Messos Master负责资源管理。

4、Executor:执行器

Application运行在Worker节点上的一个进程,该进程负责运行Task,并且负责将数据存在内存或者磁盘上,每个Application都有各自独立的一批Executor,如下图所示。

\
图6Executor运行原理

5、Worker:计算节点

集群中任何可以运行Application代码的节点,类似于Yarn中的NodeManager节点。在Standalone模式中指的就是通过Slave文件配置的Worker节点,在Spark on Yarn模式中指的就是NodeManager节点,在Spark on Messos模式中指的就是Messos Slave节点,如下图所示。

\
图7Worker运行原理

 

6、DAGScheduler:有向无环图调度器

基于DAG划分Stage 并以TaskSet的形势提交Stage给TaskScheduler;负责将作业拆分成不同阶段的具有依赖关系的多批任务;最重要的任务之一就是:计算作业和任务的依赖关系,制定调度逻辑。在SparkContext初始化的过程中被实例化,一个SparkContext对应创建一个DAGScheduler。

\
图8DAGScheduler图解

 

7、TaskScheduler:任务调度器

将Taskset提交给worker(集群)运行并回报结果;负责每个具体任务的实际物理调度。如图所示。

\
图9TaskScheduler图解

 

8、Job:作业

由一个或多个调度阶段所组成的一次计算作业;包含多个Task组成的并行计算,往往由Spark Action催生,一个JOB包含多个RDD及作用于相应RDD上的各种Operation。如图所示。

\
图10Job图解

 

9、Stage:调度阶段

一个任务集对应的调度阶段;每个Job会被拆分很多组Task,每组任务被称为Stage,也可称TaskSet,一个作业分为多个阶段;Stage分成两种类型ShuffleMapStage、ResultStage。如图所示。

\
图11Stage图解

 

Application多个job多个Stage:Spark Application中可以因为不同的Action触发众多的job,一个Application中可以有很多的job,每个job是由一个或者多个Stage构成的,后面的Stage依赖于前面的Stage,也就是说只有前面依赖的Stage计算完毕后,后面的Stage才会运行。

划分依据:Stage划分的依据就是宽依赖,何时产生宽依赖,reduceByKey, groupByKey等算子,会导致宽依赖的产生。

核心算法:从后往前回溯,遇到窄依赖加入本stage,遇见宽依赖进行Stage切分。Spark内核会从触发Action操作的那个RDD开始从后往前推,首先会为最后一个RDD创建一个stage,然后继续倒推,如果发现对某个RDD是宽依赖,那么就会将宽依赖的那个RDD创建一个新的stage,那个RDD就是新的stage的最后一个RDD。然后依次类推,继续继续倒推,根据窄依赖或者宽依赖进行stage的划分,直到所有的RDD全部遍历完成为止。

将DAG划分为Stage剖析:如上图,从HDFS中读入数据生成3个不同的RDD,通过一系列transformation操作后再将计算结果保存回HDFS。可以看到这个DAG中只有join操作是一个宽依赖,Spark内核会以此为边界将其前后划分成不同的Stage. 同时我们可以注意到,在图中Stage2中,从map到union都是窄依赖,这两步操作可以形成一个流水线操作,通过map操作生成的partition可以不用等待整个RDD计算结束,而是继续进行union操作,这样大大提高了计算的效率。

10、TaskSet:任务集

由一组关联的,但相互之间没有Shuffle依赖关系的任务所组成的任务集。如图所示。

\
图12Stage图解

 

提示:

1)一个Stage创建一个TaskSet;

2)为Stage的每个Rdd分区创建一个Task,多个Task封装成TaskSet

11、Task:任务

被送到某个Executor上的工作任务;单个分区数据集上的最小处理流程单元(单个stage内部根据操作数据的分区数划分成多个task)。如图所示。

\

 

总体如图所示:

\
图14汇总图解

 

五、Spark读写流程

5.1 写入流程

  1. RDD调用compute( )方法进行指定分区的写入。
  2. CacheManager中调用BlockManater判断数据是否已经写入,如果未写则写入。
  3. BlockManager中数据与其他节点同步。(与副本节点通讯)
  4. BlockManager根据存储级别写入指定的存储层。(存储级别决定了数据是存储在内存还是磁盘,或者是两者结合)
  5. BlockManager向主节点汇报存储状态。

5.2 读取流程

  1. RDD调用compute( )方法进行指定分区的读取。
  2. 调用BlockManager的get()进行读取
  • 在本地同步读取数据块,首先看能否在内存读取数据块,如果不能读取,则看能否从Tachyon读取数据块,如果仍不能读取,则看能否从磁盘读取数据块。
  • 如果本地没有数据,就进行远程读取:远程获取调用路径,然后getRemote调用doGetRemote,通过BlockManagerWorker.syncGetBlock从远程获取数据。

六、Spark的容错机制

分布式系统常用的容错机制

  • 数据检查点:成本高,耗费资源
  • 记录数据的更新:耗费资源较少

Spark所采用的容错机制是记录数据更新和数据检查点相结合的形式。原因如下:

  • RDD只支持粗粒度转换,即在大量记录上执行的单个操作。将创建RDD的一系列Lineage(即血统)记录下来,以便恢复丢失的分区。也就是说当子RDD分区丢失时,只要重新计算它对应的父RDD分区就可以进行恢复,这对于窄依赖来说不存在冗余计算。
  • 宽依赖也就是Shuffle Dependency,也就是发生在shuffle阶段的依赖,如果子RDD分区丢失了,那么重新计算的代价就比较大,因为有多个父RDD分区对应该子RDD分区,那么重新计算时所有的父RDD分区都得重新计算,造成了计算冗余

以下情况需要添加检查点

  • DAG中依赖线过长,如果重算,则开销太大
  • 在Shuffle Dependency上做CheckPoint(检查点)获得的收益更大

在RDD计算中,通过检查点机制进行容错,传统做检查点有两种方式:通过冗余数据和日志记录更新操作。RDD中所采用的就是通过冗余数据来缓存数据,然后对已进行冗余
操作的RDD执行删除该祖先的RDD依赖

官方建议,做检查点的RDD做好先缓存在内存中,否则需要进行重新计算

七、Spark的Shuffle机制

Shuffle中文意思就是混洗,跟MapReduce中的Shuffle的思想是相同,就是数据的重新分区和组合

Spark的Shuffle是在stage的承接阶段完成的,前面的stage会根据后面stage的分区数来将数据按照一定的规则分成相应的bucket,然后写到磁盘上。后续的stage会从元数据那里获得指定数据的所在节点,将数据拉取本地做进一步的操作

Shuffle分为两个阶段

  • Shuffle Write :Shuffle的第一步骤,就是将前一个stage中的数据写到磁盘,用于第二步的Fetch
    • 主要是在ShuffleMapTask中执行(runTask)方法
    • 如果设置了map端进行聚合的话,那么会先执行数据在map的合并,减少网络传输
    • Consolidate Shuffle相比普通Shuffle的优势在:普通shuffle产生的Shuffle文件数为map任务数 x Reduce任务数,而Consolidate Shuffle的理论Shuffle文件数为Spark Core数 x Reducer数,而概念上C_Shuffle引入了文件数组,一个Bucket不再对应一个文件,而是对应文件中的一个segment
  • Shuffle Fetch:通过本地或者远程抓取需要的数据到本节点进行计算,完成操作如保存数据到指定路径或者作为下一个Shuffle的Shuffle Write

Shuffle Aggregator并不会对所有情况下的数据进行排序,所以Aggregator分为不需要外排和需要外排两种方式

  • 不需要外排:数据全部保存在内存当中,使用AppendOnlyMap来进行数据的存储,数据是来一个处理一个,更新到map中,所以reducer的内存必须足够大,能够存储该分区的所有key和count的值
  • 需要外排:reduce类型的操作,内存没办法存放所有的key-value对,必须借助外部磁盘


原文:
https://www.jianshu.com/p/7575a0c1c73b
https://www.2cto.com/net/201810/781296.html

 

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值