Spark -八股

Spark的三种提交流程

要讨论Spark三种不同的提交流程,首先要明确Spark最基本的运行架构

Spark运行架构

Application:

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

Driver:分解任务,构成DAG,划分Stage,形成Task

Spark中的Driver即运行上述Application的main函数并创建SparkContext,创建SparkContext的目的是为了准备Spark应用程序的运行环境,在Spark中有SparkContext负责与ClusterManager通信,进行资源申请、任务的分配和监控等,当Executor部分运行完毕后,Driver同时负责将SparkContext关闭,通常用SparkContext代表Driver

Executor:

某个Application运行在worker节点上的一个进程, 该进程负责运行某些Task, 并且负责将数据存到内存或磁盘上,每个Application都有各自独立的一批Executor, 在Spark on Yarn模式下,其进程名称为CoarseGrainedExecutor Backend。一个CoarseGrainedExecutor Backend有且仅有一个Executor对象, 负责将Task包装成taskRunner,并从线程池中抽取一个空闲线程运行Task, 这个每一个oarseGrainedExecutor Backend能并行运行Task的数量取决与分配给它的cpu个数

Cluter Manager:
指的是在集群上获取资源的外部服务。目前有三种类型:

Standalon : spark原生的资源管理,由Master负责资源的分配

Apache Mesos:与hadoop MR兼容性良好的一种资源调度框架

Hadoop Yarn: 主要是指Yarn中的ResourceManager

在这里插入图片描述

基本运行流程

Spark最基本的运行流程:

Driver:创建SparkContext解析提交的Application,使其生成job,划分stage,形成task然后与Cluster Manager进行沟通申请资源;
						||
						||
Cluter Manager:接受到Driver的请求后 在集群中为其分配Executor资源供其运行Task
						||
						||
Executor:与Driver进行沟通,获取Task任务,完成任务后向其汇报

四种模式运行对比

运行模式\组件DriverCluster ManagerExecutor
本地模式运行在本地ClientSpark自带Worker
Stand Alone运行在本地ClientSpark自带Worker
YARN-Client运行在本地ClientYarn ResourceManager分配ApplicationMaster与Driver进行资源沟通NodeManager
YARN-Cluster运行在Yarn分配的Nodemanager中的ApplicationMasterYarn ResourceManagerNodeManager

本地模式

Spark本质是一套SDK,可以不依赖任何集群服务,因此可以在本地,起多个线程的方式来运行。将Spark应用以多线程的方式直接运行在本地,一般都是为了方便调试,本地模式分三类

StandAlone模式

该模式主要的节点有Client节点、Master节点和Worker节点。
其中Driver既可以运行在Master节点上中,也可以运行在本地Client端。
当用spark-shell交互式工具提交Spark的Job时,Driver在Master节点上运行
当使用spark-submit工具提交Job或者在Eclips、IDEA等开发平台上使用”new SparkConf.setManager(“spark://master:7077”)”方式运行Spark任务时,Driver是运行在本地Client端上的

运行过程如下图:
在这里插入图片描述

SparkContext连接到Master,向Master注册并申请资源(CPU Core 和Memory)

Master根据SparkContext的资源申请要求和Worker心跳周期内报告的信息决定在哪个Worker上分配资源,然后在该Worker上获取资源,然后启动StandaloneExecutorBackend;

StandaloneExecutorBackend向SparkContext注册;

SparkContext将Applicaiton代码发送给StandaloneExecutorBackend;并且SparkContext解析Applicaiton代码,构建DAG图,并提交给DAG Scheduler分解成Stage(当碰到Action操作时,就会催生Job;每个Job中含有1个或多个Stage,Stage一般在获取外部数据和shuffle之前产生),然后以Stage(或者称为TaskSet)提交给Task Scheduler,Task Scheduler负责将Task分配到相应的Worker,最后提交给StandaloneExecutorBackend执行;

StandaloneExecutorBackend会建立Executor线程池,开始执行Task,并向SparkContext报告,直至Task完成;

所有Task完成后,SparkContext向Master注销,释放资源

Yarn-Client

Yarn-Client模式中,Driver在客户端本地运行,这种模式可以使得Spark Application和客户端进行交互,因为Driver在客户端,所以可以通过webUI访问Driver的状态,默认是hadoop1:4040访问,而YARN通过 hadoop1:8088访问

在这里插入图片描述Spark Yarn Client向YARN的ResourceManager申请启动Application Master。同时在SparkContent初始化中将创建DAGScheduler和TASKScheduler等,由于我们选择的是Yarn-Client模式,程序会选择YarnClientClusterScheduler和YarnClientSchedulerBackend

ResourceManager收到请求后,在集群中选择一个NodeManager,为该应用程序分配第一个Container,要求它在这个Container中启动应用程序的ApplicationMaster,与YARN-Cluster区别的是在该ApplicationMaster不运行SparkContext,只与SparkContext进行联系进行资源的分派

Client中的SparkContext初始化完毕后,与ApplicationMaster建立通讯,向ResourceManager注册,根据任务信息向ResourceManager申请资源(Container)

一旦ApplicationMaster申请到资源(也就是Container)后,便与对应的NodeManager通信,要求它在获得的Container中启动CoarseGrainedExecutorBackend,CoarseGrainedExecutorBackend启动后会向Client中的SparkContext注册并申请Task

client中的SparkContext分配Task给CoarseGrainedExecutorBackend执行,CoarseGrainedExecutorBackend运行Task并向Driver汇报运行的状态和进度,以让Client随时掌握各个任务的运行状态,从而可以在任务失败时重新启动任务

应用程序运行完成后,Client的SparkContext向ResourceManager申请注销并关闭自己

Yarn-Cluster

在YARN-Cluster模式中,当用户向YARN中提交一个应用程序后,YARN将分两个阶段运行该应用程序:

第一个阶段是把Spark的Driver作为一个ApplicationMaster在YARN集群中先启动;

第二个阶段是由ApplicationMaster创建应用程序,然后为它向ResourceManager申请资源,并启动Executor来运行Task,同时监控它的整个运行过程,直到运行完成

YARN-cluster的工作流程分为以下几个步骤

在这里插入图片描述
Spark Yarn Client向YARN中提交应用程序,包括ApplicationMaster程序、启动ApplicationMaster的命令、需要在Executor中运行的程序等

ResourceManager收到请求后,在集群中选择一个NodeManager,为该应用程序分配第一个Container,要求它在这个Container中启动应用程序的ApplicationMaster,其中ApplicationMaster进行SparkContext等的初始化

ApplicationMaster向ResourceManager注册,这样用户可以直接通过ResourceManage查看应用程序的运行状态,然后它将采用轮询的方式通过RPC协议为各个任务申请资源,并监控它们的运行状态直到运行结束

一旦ApplicationMaster申请到资源(也就是Container)后,便与对应的NodeManager通信,要求它在获得的Container中启动CoarseGrainedExecutorBackend,CoarseGrainedExecutorBackend启动后会向ApplicationMaster中的SparkContext注册并申请Task。这一点和Standalone模式一样,只不过SparkContext在Spark Application中初始化时,使用CoarseGrainedSchedulerBackend配合YarnClusterScheduler进行任务的调度,其中YarnClusterScheduler只是对TaskSchedulerImpl的一个简单包装,增加了对Executor的等待逻辑等

ApplicationMaster中的SparkContext分配Task给CoarseGrainedExecutorBackend执行,CoarseGrainedExecutorBackend运行Task并向ApplicationMaster汇报运行的状态和进度,以让ApplicationMaster随时掌握各个任务的运行状态,从而可以在任务失败时重新启动任务

应用程序运行完成后,ApplicationMaster向ResourceManager申请注销并关闭自己

Yarn-Client VS Yarn-Cluster

理解YARN-Client和YARN-Cluster深层次的区别之前先清楚一个概念:Application Master在YARN中,每个Application实例都有一个ApplicationMaster进程,它是Application启动的第一个容器。它负责和ResourceManager打交道并请求资源,获取资源之后告诉NodeManager为其启动Container。从深层次的含义讲YARN-Cluster和YARN-Client模式的区别其实就是ApplicationMaster进程的区别

YARN-Cluster模式下,Driver运行在AM(Application Master)中,它负责向YARN申请资源,并监督作业的运行状况。当用户提交了作业之后,就可以关掉Client,作业会继续在YARN上运行,因而YARN-Cluster模式不适合运行交互类型的作业

YARN-Client模式下,Application Master仅仅向YARN请求Executor,Client会和请求的Container通信来调度他们工作,也就是说Client不能离开

Spark宽窄依赖以及Stage划分

Spark划分Stage 使得同一Stage内的数据不用落盘 增加了运行速率

宽依赖: 表示上游的Partition被下游多个Partition使用

窄依赖: 表示上游的Partition只被下游一个Partition使用

Job: 包含多个Task组成的并行计算,往往由Spark Action触发生成, 一个Application中往往会产生多个Job

Stage: 每个Job会被拆分成多组Task, 作为一个TaskSet, 其名称为Stage,Stage的划分和调度是有DAGScheduler来负责的,Stage有非最终的Stage(Shuffle Map Stage)和最终的Stage(Result Stage)两种,Stage的边界就是发生shuffle的地方

Task: 被送到某个Executor上的工作单元,但hadoopMR中的MapTask和ReduceTask概念一样,是运行Application的基本单位,多个Task组成一个Stage,而Task的调度和管理等是由TaskScheduler负责

在这里插入图片描述

Hadoop VS Spark

进程VS线程

Hadoop中的MR中每个map/reduce task都是一个java进程方式运行
好处在于进程之间是互相独立的,每个task独享进程资源,没有互相干扰,监控方便,但是问题在于task之间不方便共享数据,执行效率比较低。比如多个map task读取不同数据源文件需要将数据源加载到每个map task中,造成重复加载和浪费内存。

Spark基于Fork()线程的方式计算是为了数据共享和提高执行效率
Spark采用了线程的最小的执行单位,但缺点是线程之间会有资源竞争。

Spark基于内存

Spark 基于内存不代表Spark不落盘

Spark通过宽窄依赖划分Stage后,同一Stage内的数据在保存在内存中,只有Stage结束伴随着Shuffle才将数据落盘,也就是说Stage内形成了数据PipeLine 这样比Hadoop每个MR中途都需要落盘效率高;

Persist()

为什么需要Persist()?

spark对同一个RDD执行多次算法的默认原理:每次对一个RDD执行一个算子操作时,都会重新从源头处计算一遍。如果某一部分的数据在程序中需要反复使用,这样会增加时间的消耗。

何时需要Persist()?

  • 某个步骤计算非常耗时,需要进行persist持久化

  • Shuffle因为要进行网络传输,数据丢失后重来所花费的代价很大 需要进行Persist()

Shuffle

groupByKey、reduceByKey、map、filter、union五种哪 些会导致宽依赖,哪些会导致窄依赖?宽依赖是一对多或多对多,窄依赖是多对一或一对一,主要根据是否有shuffle过程来划分

宽依赖 groupByKey,reduceByKey

窄依赖 map、filter、union

join 可窄可宽,分区算法和分区数一样是窄依赖,否则是宽依赖

如果join操作的两个RDD有分区器,且两个分区器的分区数相同,则满足条件if (rdd.partitioner == Some(part)),此时join操作是窄依赖。

如果join操作的两个RDD没有分区器或分区数量不同,那么则不满足上面的判断语句,会执行shuffle操作。

分区数与Task数

分区数即RDD中的Partition数目 该数目和Task任务相同

RDD的分区数取决于:

1)读取文件是否可切片

2)读取HDFS文件所占的BLOCK数

3)Repartition

SparkTask:移动计算策略——任务调度数据本地性

任务本地性策略

PROCESS_LOCAL:

⼀个JVM内部,将RDD缓存、Executor、数据所在的datanode在同⼀个进程内
要处理的数据就在同⼀个本地进程中,即数据和Task在同⼀个Executor JVM中,这种情况就是RDD的数据在之前就已经被缓存过了,因为BlockManager是以Executor为单位的,所以只要Task所需要的Block在所属的Executor的BlockManager上已经被缓存,这个数据本地性就是PROCESS_LOCAL,这种是最好的locality,这种情况下数据不需要在⽹络中传输

NODE_LOCAL:

在多个Executor之间,同⼀台主机上进⾏调度数据在同⼀台节点上,但是并不不在同⼀个jvm中,⽐如数据在同⼀台节点上的另外⼀个Executor上,速度要⽐PROCESS_LOCAL略慢。还有⼀种情况是读取HDFS的块就在当前节点上,数据本地性也是NODE_LOCAL。

NO_PREF:

数据从哪⾥访问都⼀样,表⽰数据本地性⽆意义,看起来很奇怪,其实指的是从MySQL、MongoDB之类的数据源读取数据。

调度策略

优先顺序:PROCESS_LOCAL>NODE_LOCAL>RACK_LOCAL

Spark在调度程序的时候并不⼀定总是能按照计算出的数据本地性执⾏,因为即使计算出在某个Executor上执⾏时数据本地性最好,但是Executor的core也是有限的,有可能计算出TaskFoo在ExecutorBar上执⾏数据本地性最好,但是发现ExecutorBar的所有core都⼀直被⽤着腾不出资源来执⾏新来的TaskFoo,所以当TaskFoo等待⼀段时间之后发现仍然等不到资源的话就尝试降低数据本地性级别让其它的Executor去执⾏。

⽐如当前有⼀个RDD,有四个分区,称为A、B、C、D,当前Stage中这个RDD的每个分区对应的Task分别称为TaskA、TaskB、TaskC、TaskD,在之前的Stage中将这个RDD cache在了⼀台机器上的两个Executor上,称为ExecutorA、ExecutorB,每个Executor的core是2,ExecutorA上缓存了RDD的A、B、C分区,ExecutorB上缓存了RDD的D分区,然后分配Task的时候会把TaskA、TaskB、TaskC分配给ExecutorA,TaskD分配给ExecutorB

但是因为每个Executor只有两个core,只能同时执⾏两个Task,所以ExecutorA能够执⾏TaskA和TaskB,但是TaskC就只能等着,尽管它在ExecutorA上执⾏的数据本地性是PROCESS_LOCAL,但是⼈家没有资源啊,于是TaskC就等啊等,但是等了⼀会⼉它发现不太对劲,搞这个数据本地性不就是为了加快Task的执⾏速度以提⾼Stage的整体执⾏速度吗,我搁这⾥⼲等着可不能加快Stage的整体速度,我要看下边上有没有其它的Executor是闲着的,假设我在ExecutorA需要再排队10秒才能拿到core资源执⾏,拿到资源之后我需要执⾏30秒,那么我只需要找到⼀个其它的Executor,即使因为数据本地性不好但是如果我能够在40秒内执⾏完的话还是要⽐在这边继续傻等要快的,所以TaskC就给⾃⼰设定了⼀个时间,当超过n毫秒之后还等不到就放弃PROCESS_LOCAL级别,转⽽尝试NODE_LOCAL级别的Executor,然后它看到了ExecutorB,ExecutorB和ExecutorA在同⼀台机器上,只是两个不同的jvm,所以在ExecutorB上执⾏需要从ExecutorA上拉取数据,通过BlockManager的getRemote,底层通过BlockTransferService去把数据拉取过来,因为是在同⼀台机器上的两个进程之间使⽤socket数据传输,⾛的应该是回环地址,速度会⾮常快,所以对于这种数据存储在同⼀台机器上的不同Executor上因为降级导致的NODE_LOCAL的情况,理论上并不会⽐

PROCESS_LOCAL慢多少,TaskC在ExecutorB上执⾏并不会⽐ExecutorA上执⾏慢多少。但是对于⽐如HDFS块存储在此节点所以将Task分配到此节点的情况导致的NODE_LOCAL,因为要跟HDFS交互,还要读取磁盘⽂件,涉及到了⼀些I/O操作,这种情况就会耗费较长时间,相⽐较于PROCESS_LOCAL级别就慢上不少了。

Spark的三种Shuffle

首先我们需要明确Shuffle就是把MapTask获取的数据分类发放给ReduceTask 一个Executor可以同时拥有N个CORES
而每个CORES只能执行一个TASK线程

未优化的HashShuffle

每个MapTask内部进行Hash运算 下游有几个ReduceTask 就把MapTask内的数据分成几类
然后发往下游
在这里插入图片描述缺陷:同一个Executor下 每个Task都要维护一份shuffle结果

优化后的HashShuffle

在这里插入图片描述在这里插入图片描述缺陷:这样产生的数据虽然优化了内存的使用 但是当文件变大时 会产生大量的磁盘文件
落盘之后发往下游Task就会非常耗时(一不是顺序存储二没有索引)

SortedShuffle

为了解决上述问题 提出了SortedShuffle
我们注意关键词:写入内存排序 建立索引并落盘 ==> 这不标标准准的Kafka管理Partititon的方法嘛!
在这里插入图片描述在这里插入图片描述

bypassShuffle

虽然SortedShuffle解决文件读取的问题 但如果MapTask本身的数据量就不大 还要花费大力气来进行排序 反而会降低Shuffle的性能

所以设置了一个阈值 只有当下游ReduceTask数量超过这个阈值或者 MapTask的数据量过大才会使用SortedShuffle 不然的话还是使用HashShuffle

这个Shuffle模式就是ByPassShuffle

算子

RDD根据数据处理方式的不同将算子整体上划分为:

	Value类型
	双Value类型
	Key-Value类型

在这里插入图片描述

Checkpoint和Persist和Cache

Checkpoint、Persist、Cache的关系

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Spark任务的详细执行流程如下: 1. 创建SparkContext:首先,Spark应用程序需要创建一个SparkContext对象,它是与集群通信的主要入口点。 2. 创建RDD:在Spark中,数据被组织成弹性分布式数据集(RDD)。RDD可以从外部存储系统(如HDFS)中读取数据,也可以通过对已有RDD进行转换操作来创建。 3. 转换操作:Spark提供了一系列转换操作,如map、filter、reduce等。这些操作可以对RDD进行转换,生成新的RDD。转换操作是惰性求值的,即不会立即执行,而是记录下来以便后续执行。 4. 行动操作:当需要从RDD中获取结果时,需要执行行动操作。行动操作会触发Spark作业的执行,并将结果返回给驱动程序。 5. 任务划分:Spark将作业划分为一系列任务,每个任务处理RDD的一个分区。任务划分是根据数据的分区情况和可用的计算资源进行的。 6. 任务调度:Spark将任务调度到集群中的可用计算节点上执行。任务调度器负责将任务分配给可用的Executor,并监控任务的执行情况。 7. 任务执行:每个Executor会为分配给它的任务创建一个或多个线程,并在这些线程上执行任务。任务执行过程中,Executor会将数据从内存或磁盘中读取到计算节点上,并进行计算操作。 8. 数据传输:在任务执行过程中,Spark会根据需要将数据从一个节点传输到另一个节点。这种数据传输可以是节点内的数据传输,也可以是跨节点的数据传输。 9. 结果返回:当任务执行完成后,结果会返回给驱动程序。驱动程序可以将结果保存到外部存储系统,或者进行进一步的处理和分析。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值