spark 特点,优点
快 基于内存,
api算子多,
支持sql,流处理,机器学习
支持yarn调用
Spark的运行流程
上图为Spark通用运行流程图,体现了基本的Spark应用程序在部署中的基本提交流程。 这个流程是按照如下的核心步骤进行工作的: 1)任务提交后,都会先启动Driver程序;
2)随后Driver向集群管理器注册应用程序;
3)之后集群管理器根据此任务的配置文件分配Executor并启动;
4)Driver开始执行main函数,Spark查询为懒执行,当执行到Action算子时开始反向推算,根据宽依赖进 行Stage的划分,随后每一个Stage对应一个Taskset,Taskset中有多个Task,查找可用资源Executor进行 调度;
5)根据本地化原则,Task会被分发到指定的Executor去执行,在任务执行的过程中,Executor也会不断 与Driver进行通信,报告任务运行情况。
DAG的生成
DAG(Directed Acyclic Graph)叫做有向无环图,原始的RDD通过一系列的转换就就形成了DAG,根据RDD之 间的依赖关系的不同将DAG划分成不同的Stage,对于窄依赖,partition的转换处理在Stage中完成计算。 对于宽依赖,由于有ShuVle的存在,只能在parent RDD处理完成后,才能开始接下来的计算,因此宽依 赖是划分Stage的依据。
窄依赖:指的是每一个父RDD的Partition最多被子RDD的一个Partition使用
宽依赖:指的是多个子RDD的Partition会依赖同一个父RDD的Partition
DAGScheduler 这个类的任务就是用来划分Stage任务的
Spark SQL:提供通过Apache Hive的SQL变体Hive查询语言(HiveQL)与Spark进行交互的API。每个数据 库表被当做一个RDD,Spark SQL查询被转换为Spark操作。
Spark on standalone模型、YARN架构模型(画架构图)
可回答:Spark client和cluster模式有什么区别
1、Spark on Standalone运行过程(架构)
Standalone模式是Spark实现的资源调度框架,其主要的节点有Client节点、Master节点和Worker节点。 其中Driver既可以运行在Master节点上中,也可以运行在本地Client端。当用spark-shell交互式工具提交 Spark的Job时,Driver在Master节点上运行;当使用spark-submit工具提交Job或者在Eclipse、IDEA等开发 平台上使用 new SparkConf().setMaster(“spark://master:7077”) 方式运行Spark任务时,Driver 是运行在本地Client端上的。
运行流程如下: 1)我们提交一个任务,任务就叫Application; 2)初始化程序的入口SparkContext;
初始化DAG Scheduler
初始化Task Scheduler
3)Task Scheduler向master去进行注册并申请资源(CPU Core和Memory);
4)Master根据SparkContext的资源申请要求和Worker心跳周期内报告的信息决定在哪个Worker上分配资 源,然后在该Worker上获取资源,然后启动StandaloneExecutorBackend;顺便初始化好了一个线程池;
5)StandaloneExecutorBackend向Driver(SparkContext)注册,这样Driver就知道哪些Executor为他进行服 务了。到这个时候其实我们的初始化过程基本完成了,我们开始执行transformation的代码,但是代码并 不会真正的运行,直到我们遇到一个action操作。生产一个job任务,进行stage的划分;
6)SparkContext将Applicaiton代码发送给StandaloneExecutorBackend;并且SparkContext解析 Applicaiton代码,构建DAG图,并提交给DAG Scheduler分解成Stage(当碰到Action操作时,就会催生 Job;每个Job中含有1个或多个Stage,Stage一般在获取外部数据和shuVle之前产生);
7)将Stage(或者称为TaskSet)提交给Task Scheduler。Task Scheduler负责将Task分配到相应的 Worker,最后提交给StandaloneExecutorBackend执行;
8)对task进行序列化,并根据task的分配算法,分配task; 9)executor对接收过来的task进行反序列化,把task封装成一个线程; 10)开始执行Task,并向SparkContext报告,直至Task完成; 11)资源注销
2、Spark on YARN运行过程(架构)
Spark on YARN模式根据Driver在集群中的位置分为两种模式:一种是YARN-Client模式,另一种是YARN- Cluster(或称为YARN-Standalone模式)。
YARN-Client模式
Yarn-Client模式中,Driver在客户端本地运行,这种模式可以使得Spark Application和客户端进行交互, 因为Driver在客户端,所以可以通过webUI访问Driver的状态,默认是 http://hadoop1:4040 访问,而 YARN通过 http:// hadoop1:8088 访问。
YARN-client的工作流程分为以下几个步骤:
1)Spark Yarn Client向YARN的ResourceManager申请启动Application Master。同时在SparkContent初始化 中将创建DAGScheduler和TASKScheduler等,由于我们选择的是Yarn-Client模式,程序会选择 YarnClientClusterScheduler和YarnClientSchedulerBackend;
2)ResourceManager收到请求后,在集群中选择一个NodeManager,为该应用程序分配第一个 Container,要求它在这个Container中启动应用程序的ApplicationMaster,与YARN-Cluster区别的是在该 ApplicationMaster不运行SparkContext,只与SparkContext进行联系进行资源的分派;
3)Client中的SparkContext初始化完毕后,与ApplicationMaster建立通讯,向ResourceManager注册,根 据任务信息向ResourceManager申请资源(Container);
4)一旦ApplicationMaster申请到资源(也就是Container)后,便与对应的NodeManager通信,要求它在 获得的Container中启动启动CoarseGrainedExecutorBackend,CoarseGrainedExecutorBackend启动后会向 Client中的SparkContext注册并申请Task;
5)Client中的SparkContext分配Task给CoarseGrainedExecutorBackend执行, CoarseGrainedExecutorBackend运行Task并向Driver汇报运行的状态和进度,以让Client随时掌握各个任务 的运行状态,从而可以在任务失败时重新启动任务;
6)应用程序运行完成后,Client的SparkContext向ResourceManager申请注销并关闭自己。
YARN-Cluster模式
在YARN-Cluster模式中,当用户向YARN中提交一个应用程序后,YARN将分两个阶段运行该应用程序:第 一个阶段是把Spark的Driver作为一个ApplicationMaster在YARN集群中先启动;第二个阶段是由 ApplicationMaster创建应用程序,然后为它向ResourceManager申请资源,并启动Executor来运行Task, 同时监控它的整个运行过程,直到运行完成。
YARN-cluster的工作流程分为以下几个步骤:
1)Spark Yarn Client向YARN中提交应用程序,包括ApplicationMaster程序、启动ApplicationMaster的命 令、需要在Executor中运行的程序等;
2)ResourceManager收到请求后,在集群中选择一个NodeManager,为该应用程序分配第一个 Container,要求它在这个Container中启动应用程序的ApplicationMaster,其中ApplicationMaster进行 SparkContext等的初始化;
3)ApplicationMaster向ResourceManager注册,这样用户可以直接通过ResourceManage查看应用程序的 运行状态,然后它将采用轮询的方式通过RPC协议为各个任务申请资源,并监控它们的运行状态直到运 行结束;
4)一旦ApplicationMaster申请到资源(也就是Container)后,便与对应的NodeManager通信,要求它在 获得的Container中启动启动CoarseGrainedExecutorBackend,CoarseGrainedExecutorBackend启动后会向 ApplicationMaster中的SparkContext注册并申请Task。这一点和Standalone模式一样,只不过 SparkContext在Spark Application中初始化时,使用CoarseGrainedSchedulerBackend配合 YarnClusterScheduler进行任务的调度,其中YarnClusterScheduler只是对TaskSchedulerImpl的一个简单包 装,增加了对Executor的等待逻辑等;
5)ApplicationMaster中的SparkContext分配Task给CoarseGrainedExecutorBackend执行, CoarseGrainedExecutorBackend运行Task并向ApplicationMaster汇报运行的状态和进度,以让 ApplicationMaster随时掌握各个任务的运行状态,从而可以在任务失败时重新启动任务;
6)应用程序运行完成后,ApplicationMaster向ResourceManager申请注销并关闭自己。
3、YARN-Client 与 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的yarn-cluster涉及的参数有哪些
--class org.apache.spark.examples.SparkPi \
--master yarn \
--deploy-mode cluster \
--driver-memory 1g \
--executor-memory 1g \
--executor-cores 2 \
-queue default \
参数 | 解释 |
--class | 程序的main方法所在的类 |
--master | 指定 Master 的地址 |
--deploy-mode | 指定运行模式(client/cluster) |
--driver-memory | Driver运行所需要的内存, 默认1g |
--executor-memory | 指定每个 executor 可用内存为 2g, 默认1g |
--executor-cores | 指定每一个 executor 可用的核数 |
--queue | 指定任务的对列 |
Spark的阶段划分
这里说下Driver的工作流程,Driver线程主要是初始化SparkContext对象,准备运行所需的上下文,然后 一方面保持与ApplicationMaster的RPC连接,通过ApplicationMaster申请资源,另一方面根据用户业务逻 辑开始调度任务,将任务下发到已有的空闲Executor上。
当ResourceManager向ApplicationMaster返回Container资源时,ApplicationMaster就尝试在对应的 Container上启动Executor进程,Executor进程起来后,会向Driver反向注册,注册成功后保持与Driver的 心跳,同时等待Driver分发任务,当分发的任务执行完毕后,将任务状态上报给Driver。
当Driver起来后,Driver则会根据用户程序逻辑准备任务,并根据Executor资源情况逐步分发任务。
一个RDD任务分为:Application、Job、Stage 和 Task。 Application:初始化一个 SparkContext 就生成一个 Application; Job:以Action方法为界,遇到一个Action方法则触发一个Job; Stage:Job的子集,以RDD宽依赖(即ShuVle)为界,遇到ShuVle做一次划分;
Task:Stage的子集,以并行度(分区数)来衡量,分区数是多少,则有多少个task。
Spark RDD通过其Transactions操作,形成了RDD血缘(依赖)关系图,即DAG,最后通过Action的调用, 触发Job并调度执行,执行过程中会创建两个调度器:DAGScheduler和TaskScheduler。
DAGScheduler负责Stage级的调度,主要是将job切分成若干Stages,并将每个Stage打包成TaskSet交 给TaskScheduler调度。 TaskScheduler负责Task级的调度,将DAGScheduler给过来的TaskSet按照指定的调度策略分发到 Executor上执行,调度过程中SchedulerBackend负责提供可用资源,其中SchedulerBackend有多种 实现,分别对接不同的资源管理系统。
Driver初始化SparkContext过程中,会分别初始化DAGScheduler、TaskScheduler、SchedulerBackend以及 HeartbeatReceiver,并启动SchedulerBackend以及HeartbeatReceiver。SchedulerBackend通过 ApplicationMaster申请资源,并不断从TaskScheduler中拿到合适的Task分发到Executor执行。 HeartbeatReceiver负责接收Executor的心跳信息,监控Executor的存活状况,并通知到TaskScheduler。
Spark join的分类
Spark join方式主要有以下三种:
hash join是传统数据库中的单机join算法,在分布式环境下需要经过一定的分布式改造,说到底就是尽 可能利用分布式计算资源进行并行化计算,提高总体效率。hash join分布式改造一般有两种经典方案:
broadcast hash join:将其中一张小表广播分发到另一张大表所在的分区节点上,分别并发地与其上的分 区记录进行hash join。broadcast适用于小表很小,可以直接广播的场景。
1、Broadcast Hash Join
broadcast hash join可以分为两步:
1)broadcast阶段:将小表广播分发到大表所在的所有主机。广播算法可以有很多,最简单的是先发给 driver,driver再统一分发给所有executor;要不就是基于bittorrete的p2p思路;
2)hash join阶段:在每个executor上执行单机版hash join,小表映射,大表试探。
适用于小表与大表的join,SparkSQL规定broadcast hash join执行的基本条件为被广播小表必须小于参数 spark.sql.autoBroadcastJoinThreshold,默认为10M,本质上是去用空间换时间,
缺点 : 小表需要建立索引,由于需要在 Driver 节点进行 collect ,所以需要保证 Driver 节点内 存充足,可在 spark-commit 提交执行任务时,通过设置 driver-memory 调节 Driver 节点大小。Map-Join 同时需要将经过 Map 广播到不同的 executor ,供 Task 获取数据进行连接并输出结果。所以 executor 也需要保证内存充足,可在 spark-commit 提交执行任务时,通过设置 --executor-memory 调节 Driver 节点大小。
2、Shuffle Hash Join
spark.shuffle.consolidateFiles 。该参数默认值为false,
shuffle hash join:一旦小表数据量较大,此时就不再适合进行广播分发。这种情况下,可以根据join key相同必然分区相同的原理,将两张表分别按照join key进行重新组织分区,这样就可以将join分而治 之,划分为很多小join,充分利用集群资源并行化。
适合于没有特别小的两个表进行关联的时候,默认设置的Shuffle partition的个数是200,也就是分了200 个区,然后两张表的key值分别去基于200做hash取余然后散步在每个区域中了,这样的思想先把相近的 合并在一个区内,再在每个分区内去做比较key值的等值比较,就避免了大范围的遍历比较,节省了时 间和内存。
缺点,生产文件过多大量小文件随机读写磁盘开销,数据块写入缓存,对内存造成压力。
3、Sort-Merge Join
SparkSQL对两张大表join采用了全新的算法:sort-merge
1)Shuffle阶段:将两张大表根据join key进行重新分区,两张表数据会分布到整个集群,以便分布式并 行处理;
2)sort阶段:对单个分区节点的两表数据,分别进行排序;
3)merge阶段:对排好序的两张分区表数据执行join操作。join操作很简单,分别遍历两个有序序列,碰 到相同join key就merge输出,否则取更小一边
这种适用于关联的两张表都特别大时,使用上述的两种方法加载到内存的时候对于内存的压力都非常大 时,因此在2方法的基础上,hash取余之后还要分别对两张表的key值进行排序,这样去做等值比较的时 候就不需要将某一方的全部数据都加载到内存进行计算了,只需要取一部分就能知道是否有相等的(比 如按升序排列,某个值明显比它大了,后面肯定就不会有相等的,就不用继续比较了,节省了时间和内 存),也就是在进行等值比较的时候即用即丢的。这个方法在前面进行排序的时候可能会消耗点时间, 但相对于后面的时间来说,总体是大大节省了时间。
2)bypass SortShuffle bypass运行机制的触发条件如下:
Shuffle reduce task数量小于spark.Shuffle.sort.bypassMergeThreshold参数的值,默认为200
不是聚合类的Shuffle算子(比如reduceByKey)
而该机制与普通SortShuffleManager运行机制的不同在于:不会进行排序。也就是说,启用该机制的最 大好处在于,Shuffle write过程中,不需要进行数据的排序操作,也就节省掉了这部分的性能开销。
优点:
小文件的数量大量减少,Mapper 端的内存占用变少;
Spark 不仅可以处理小规模的数据,即使处理大规模的数据,也不会很容易达到性能瓶颈。
缺点:
如果 Mapper 中 Task 的数量过大,依旧会产生很多小文件,此时在 Shuffle 传数据的过程中到 Reducer 端, Reducer 会需要同时大量地记录进行反序列化,导致大量内存消耗和 GC 负担巨大, 造成系统缓慢,甚至崩溃;
强制了在 Mapper 端必须要排序,即使数据本身并不需要排序; 它要基于记录本身进行排序,这就是 Sort-Based Shuffle 最致命的性能消耗。
spark数据倾斜
1.预先处理,过滤脏数据
2.提高reduce端并行度reduceByKey(500)
3.使用随机key实现双重聚合(仅仅适用于聚合类 的shuffle操作groupByKey、reduceByKey)
通过map算子给每个数据的key添加随机数前缀,对key进行打散,将原先一样的key变成不一样的 key,然后进行第一次聚合,这样就可以让原本被一个task处理的数据分散到多个task上去做局部聚合; 随后,去除掉每个key的前缀,再次进行聚合
4.将reduce join转换为map join
不使用join算子进行连接操作,而使用Broadcast变量与map类算子实现join操作,进而完全规避掉shuffle 类的操作,彻底避免数据倾斜的发生和出现。
将较小RDD中的数据直接通过collect算子拉取到Driver端的 内存中来,然后对其创建一个Broadcast变量;接着对另外一个RDD执行map类算子,在算子函数内,从 Broadcast变量中获取较小RDD的全量数据,与当前RDD的每一条数据按照连接key进行比对,如果连接 key相同的话,那么就将两个RDD的数据用你需要的方式连接起来
宽依赖,窄依赖
相比于宽依赖,窄依赖对优化还有以下几点优势:
宽依赖往往对应着Shuffle操作,需要在运行过程中将同一个父RDD的分区传入到不同的子RDD分区 中,中间可能涉及多个节点之间的数据传输;而窄依赖的每个父RDD的分区只会传入到一个子RDD 分区中,通常可以在一个节点内完成转换。
当RDD分区丢失时(某个节点故障),spark会对数据进行重算。
对于窄依赖,由于父RDD的一个分区只对应一个子RDD分区,这样只需要重算和子RDD分区对 应的父RDD分区即可,所以这个重算对数据的利用率是100%的;
对于宽依赖,重算的父RDD分区对应多个子RDD分区,这样实际上父RDD 中只有一部分的数据 是被用于恢复这个丢失的子RDD分区的,另一部分对应子RDD的其它未丢失分区,这就造成了 多余的计算;
更一般的,宽依赖中子RDD分区通常来自多个父RDD分区,极端情况下,所有的 父RDD分区都要进行重新计算。
为什么要划分Stage,
stage数量=宽依赖数据+1
由于一个job任务中可能有大量的宽窄依赖,窄依赖不会产生Shuffle,宽依赖会产生Shuffle。后期划分完 stage之后,在同一个stage中只有窄依赖,并没有宽依赖,这些窄依赖对应的task就可以相互独立的取运 行。划分完stage之后,它内部是有很多可以并行运行task。
Spark容错机制?
1、Lineage机制(简称血统机制)
RDD的Lineage机制是将粗颗粒度相同的操作(map flatmap filter之类的)记录下来,万一RDD信息丢失 可通过Lineage获取信息从新获得原来丢失的RDD
缺点:lineage如果过长 影响性能,开销太大
2、checkpoint机制 将RDD的结果存入HDFS,以文件的形式存在
3、cache机制
将结果存入内存
4、persist机制
将结果存入磁盘
checkpoint 和 cache 和 persist的异同 相同点:都要在action算子之后触发 不同点:
checkpoint:
是存在HDFS上的,更加安全可靠,并且checkpoint会改变依赖关系,并且之后会触发一个job操作
persist和cache
不改变依赖关系,运行完成后缓存数据自动消失
Spark SQL的执行原理?
近似于关系型数据库,见下图,Spark SQL语句由Projection(a1,a2,a3)、Data Source(tableA)、 Filter(condition)组成,分别对应SQL查询过程中的Result、Data Source、Operation,也就是说,SQL 语句按指定次序来描述,如Result->Data Source->Operation。
执行Spark SQL语句的顺序如下:
1)对读入的SQL语句进行解析(Parse),分辨出SQL语句中的关键词(如SELECT、FROM、Where)、 表达式、Projection、Data Source等,从而判断SQL语句是否规范。
2)将SQL语句和数据库的数据字典(列、表、视图等)进行绑定(Bind),如果相关的Projection、 Data Source等都存在的话,就表示这个SQL语句是可以执行的。
3)选择最优计划。一般的数据库会提供几个执行计划,这些计划一般都有运行统计数据,数据库会在 这些计划中选择一个最优计划(Optimize)。
4)计划执行(Execute)。计划执行案Operation->Data Source->Result的次序来执行,在执行过程中有时 候甚至不需要读取物理表就可以返回结果,如重新运行执行过的SQL语句,可直接从数据库的缓冲池中 获取返回结果。
Spark SQL的数据倾斜解决方案:
聚合源数据:Spark Core和Spark SQL没有任何区别 过滤导致倾斜的key:在sql中用where条件 提高Shuffle并行度:groupByKey(1000),spark.sql.Shuffle.partitions(默认是200) 双重groupBy:改写SQL,两次groupBy
reduce join转换为map join:spark.sql.autoBroadcastJoinThreshold(默认是10485760);可以自己 将表做成RDD,自己手动去实现map join;SparkSQL内置的map join,默认如果有一个10M以内的小 表,会将该表进行broadcast,然后执行map join;调节这个阈值,比如调节到20M、50M、甚至 1G。
采样倾斜key并单独进行join:纯Spark Core的一种方式,sample、filter等算子 随机key与扩容表:Spark SQL+Spark Core
Spark checkpoint
可回答:Spark Streaming的checkpoint 问过的一些公司:顺丰,竞技世界,哈啰 参考答案: 1、先看下checkpoint到底是什么?
1)Spark在生产环境下经常会面临Tranformations的RDD非常多(例如,一个Job中包含10 000个RDD)或 者具体Tranformation产生的RDD本身计算特别复杂和耗时(例如,计算时常超过1h),此时我们必须考 虑对计算结果数据的持久化。
2)Spark擅长多步骤迭代,同时擅长基于Job的复用,这时如果能够对曾经计算的过程产生的数据进行 复用,就可以极大地提升效率。
3)如果采用persist把数据放在内存中,虽然是最快速的,但是也是最不可靠的。如果放在磁盘上,也不 是完全可靠的。例如,磁盘会损坏,管理员可能清空磁盘等。
4)checkpoint的产生就是为了相对更加可靠地持久化数据,checkpoint可以指定把数据放在本地并且是 多副本的方式,但是在正常的生产情况下是放在HDFS,这就自然地借助HDFS高容错、高可靠的特征完 成了最大化的、可靠的持久化数据的方式。
5)为确保RDD复用计算的可靠性,checkpoint把数据持久化到HDFS中,保证数据最大程度的安全性。
6)checkpoint就是针对整个RDD计算链条中特别需要数据持久化的环节(后面会反复使用当前环节的 RDD)开始基于HDFS等的数据持久化复用策略,通过对RDD启动checkpoint机制来实现容错和高可用。
至于Spark中checkpoint源码详解可以参考《Spark大数据商业实战三部曲》9.2小结,讲的很详细
2、checkpoint运行流程图如下图所示
通过SparkContext设置Checkpoint数据保存的目录,RDD调用checkpoint方法,生产 RDDCheckpointData,当RDD上运行一个Job后,就会立即触发RDDCheckpointData中的checkpoint方法, 在其内部会调用doCheckpoint;然后调用ReliableRDDCheckpointData的doCheckpoint; ReliableCheckpointRDD的writeRDDToCheckpointDirectory的调用;在writeRDDToCheckpointDirectory方法 内部会触发runJob,来执行把当前的RDD中的数据写到Checkpoint的目录中,同时会产生 ReliableCheckpointRDD实例。
checkpoint保存在HDFS中,具有多个副本;persist保存在内存中或者磁盘中。在Job作业调度的时候, checkpoint沿着finalRDD的“血统”关系lineage从后往前回溯向上查找,查找哪些RDD曾标记为要进行 checkpoint,标记为checkpointInProgress;一旦进行checkpoint,RDD所有父RDD就被清空。