![3b50e05317e1590b819a41a4c077855b.png](https://i-blog.csdnimg.cn/blog_migrate/43b9494be92fd229d3ee589b13c3adae.jpeg)
1.基本定义
Spark是分布式的基于内存的迭代式计算框架,当然它也基于磁盘做迭代计算,具说Spark基于磁盘做迭代计算也会比基于磁盘迭代的MapReduce框架快10余倍;而基于内存迭代计算则比MapReduce迭代快100倍以上。
2.底层原理
![33edad9e8533dd569877c22b728e1b6f.png](https://i-blog.csdnimg.cn/blog_migrate/05c570d31566e820418f7a897d7c3bc4.jpeg)
(1)Application
- 指的是用户编写的Spark应用程序,包含了Driver功能代码和分布在集群中多个节点上运行的Executor代码
(2)Driver
- Spark中的Driver即运行上述Application的Main()函数并且创建SparkContext,其中创建SparkContext的目的是为了准备Spark应用程序的运行环境。
- 在Spark中由SparkContext负责和ClusterManager通信,进行资源的申请、任务的分配和监控等;当Executor部分运行完毕后,Driver负责将SparkContext关闭。
(3)DAGScheduler
- DAGScheduler把一个spark作业转换成成stage的DAG(Directed Acyclic Graph有向无环图)
- 根据RDD和stage之间的关系,找出开销最小的调度方法,然后把stage以TaskSet的形式提交给TaskScheduler。
(4)TaskScheduler
- TaskScheduler维护所有TaskSet,当Executor向Driver发送心跳时,TaskScheduler会根据其资源剩余情况分配相应的Task。
- 另外TaskScheduler还维护着所有Task的运行状态,重试失败的Task。
(5)Executor
- Application运行在Worker节点上的一个进程,该进程负责运行Task,并且负责将数据存在内存或者磁盘上,每个Application都有各自独立的一批Executor,
(6) Task
- 被送到某个Executor上的工作任务;单个分区数据集上的最小处理流程单元。
(7) Job
- 由一个或多个调度阶段所组成的一次计算作业;包含多个Task组成的并行计算
- 一个JOB包含多个RDD及作用于相应RDD上的各种Operation
(8) Stage
- 一个任务集对应的调度阶段
- 每个Job会被拆分很多组Task,每组任务被称为Stage,也可称TaskSet,一个作业分为多个阶段;Stage分成两种类型ShuffleMapStage、ResultStage。
(9)Rdd
- RDD(Resilient Distributed Datasets弹性分布式数据集),是spark中最重要的概念
- 可以简单的把RDD理解成一个提供了许多操作接口的数据集合,和一般数据集不同的是,其实际数据分布存储于一批机器中(内存或磁盘中)
(10)DataFrame
- DataFrame是一个分布式集合,其中数据被组织为命名的列。它概念上等价于关系数据库中的表,但底层做了更多的优化。
- DataFrame可以从很多数据源构建,比如:已经存在的RDD、结构化文件、外部数据库、Hive表。
(11)DataSet
- Dataset可以认为是DataFrame的一个特例,主要区别是Dataset每一个record存储的是一个强类型值而不是一个Row。
- DataSet可以在编译时检查类型,并且是面向对象的编程接口
3.重要概念
(1)spark on yarn 执行流程
![c939636a9615c10e0320d9d4f230fa56.png](https://i-blog.csdnimg.cn/blog_migrate/f65f2023078da2b2a7072288105f2fe9.jpeg)
- Spark Yarn Client向YARN的ResourceManager申请启动Application Master。同时在SparkContent初始化中将创建DAGScheduler和TASKScheduler等
- ResourceManager收到请求后,在集群中选择一个NodeManager,为该应用程序分配第一个Container,要求它在这个Container中启动应用程序的ApplicationMaster
- Client中的SparkContext初始化完毕后,与ApplicationMaster建立通讯,向ResourceManager注册,根据任务信息向ResourceManager申请资源(Container);
- 一旦ApplicationMaster申请到资源(也就是Container)后,便与对应的NodeManager通信,要求它在获得的Container中启动Executor,Executor启动后会向Client中的SparkContext注册并申请Task;
- Client中的SparkContext分配Task给Executor执行,Executor运行Task并向Driver汇报运行的状态和进度,以让Client随时掌握各个任务的运行状态,从而可以在任务失败时重新启动任务;
- 应用程序运行完成后,Client的SparkContext向ResourceManager申请注销并关闭自己;
(2)深入理解Rdd
(2.1)结构化理解
![afa84d3a44a3281d812fe0906e163ec4.png](https://i-blog.csdnimg.cn/blog_migrate/c3705c0af42af61d62f5a7be5684507d.jpeg)
(2.2)基础特点
- 是一个分区的只读记录的集合;
- 一个具有容错机制的特殊集;
- 只能通过在稳定的存储器或其他RDD上的确定性操作(转换)来创建;
- 可以分布在集群的节点上,以函数式操作集合的方式,进行各种并行操作
(2.3)弹性特点
- 基于Lineage的高效容错(第n个节点出错,会从第n-1个节点恢复,血统容错);
- Task如果失败会自动进行特定次数的重试(默认4次);
- Stage如果失败会自动进行特定次数的重试(可以值运行计算失败的阶段),只计算失败的数据分片;
- 数据调度弹性:DAG TASK 和资源管理无关;
- checkpoint;
- 自动的进行内存和磁盘数据存储的切换;
(2.4)partition
- RDD是一个只读的有属性的数据集。属性用来描述当前数据集的状态,数据集是由数据的分区(partition)组成。
- RDD 内部的数据集合在逻辑上和物理上被划分成多个小子集合,这样的每一个子集合我们将其称为分区(partitions)
- 分区的个数会决定并行计算的粒度,而每一个分区数值的计算都是在一个单独的任务中进行,因此并行任务的个数,也是由 RDD分区的个数决定的。
(2.5)dependencies
- 每次transformations操作时,都是重新创建了一个新的RDD2,这个RDD2时基于原有的RDD1,RDD1是RDD2的Parents,也就是说这个RDD2依赖于RDD1。这些依赖描述了RDD的Lineage(血统)
- 如果父RDD的每个分区最多只能被子RDD的一个分区使用,我们称之为(narrow dependency)窄依赖;
- 若一个父RDD的每个分区可以被子RDD的多个分区使用,我们称之为(wide dependency)宽依赖,在源代码中方法名为ShuffleDependency,顾名思义这之中还需要Shuffle操作
![ce9045f8d12d3aeba5373175b88fd4f1.png](https://i-blog.csdnimg.cn/blog_migrate/b82f7166941cee7af716c361d436a7fa.jpeg)
(2.6)Checkpoint
尽管当一个RDD出现问题可以由它的依赖也就是Lineage信息可以用来故障恢复,但对于那些Lineage链较长的RDD来说,这种恢复可能很耗时。
Checkpoint是Spark提供的一种缓存机制,当需要计算的RDD过多时,为了避免重新计算之前的RDD,可以对RDD做Checkpoint处理,检查RDD是否被物化或计算,并将结果持久化到磁盘或HDFS。
- Checkpoint会把当前RDD保存到一个目录中。
- Checkpoint的时候,会把所有依赖的父级rdd信息清除掉。
- Checkpoint不会马上执行,要触发action操作的时候才会执行。
- 因为 Checkpoint会清除父级RDD的信息,所以在Checkpoint应该先做persist(持久化)操作,否则就要重新计算一遍。
- 一般来说,Lineage链较长、宽依赖的RDD需要采用检查点机制。
(3)Shuffle详解
Stage之间就是Shuffle,如图groupby 之后的 Map 操作,为了计算相同 key 下的元素个数,需要把相同 key 的元素聚集到同一个 partition 下,所以造成了数据在内存中的重新分布,即 shuffle 操作 。
shuffle 操作是 spark 中最耗时的操作,应尽量避免不必要的 shuffle,Stage1,Stage2和Stag3之间就会产生Shuffle
![a118734780a8d2d32912e1c5d8cde0b7.png](https://i-blog.csdnimg.cn/blog_migrate/c2811e9056789997615f695683fd02c8.jpeg)
在Spark的中,负责shuffle过程的执行、计算和处理的组件主要就是ShuffleManager,也即shuffle管理器。spark的Shuffle有Hash Shuffle和Sort Shuffle两种
(3.1)聚合
reduceByKey会将上一个RDD中的每一个key对应的所有value聚合成一个value,然后生成一个新的RDD,元素类型是对的形式,这样每一个key对应一个聚合起来的value。
- Shuffle Write:上一个stage的每个map task就必须保证将自己处理的当前分区的数据相同的key写入一个分区文件中,可能会写入多个不同的分区文件中。
- Shuffle Read:reduce task就会从上一个stage的所有task所在的机器上寻找属于己的那些分区文件,这样就可以保证每一个key所对应的value都会汇聚到同一个节点上去处理和聚合。
(3.2)hashshuffle
- 执行流程
- 每一个map task将不同结果写到不同的buffer中,每个buffer的大小为32K。buffer起到数据缓存的作用。新写的磁盘小文件会追加内容。
- 每个buffer文件最后对应一个磁盘小文件。
- reduce task来拉取对应的磁盘小文件
- 总结
- maptask的计算结果会根据分区器(默认是hashPartitioner)来决定写入到哪一个磁盘小文件中去。ReduceTask会去Map端拉取相应的磁盘小文件。
- 产生的磁盘小文件的个数:M(map task的个数)*R(reduce task的个数)
- 问题:
- 在Shuffle Write过程中会产生很多写磁盘小文件的对象。
- 在Shuffle Read过程中会产生很多读取磁盘小文件的对象。
- 在JVM堆内存中对象过多会造成频繁的gc,gc还无法解决运行所需要的内存 的话,就会OOM
- 在数据传输过程中会有频繁的网络通信,频繁的网络通信出现通信故障的可能性大大增加
(3.3)sortshuffle
- 执行流程
- map task 的计算结果会写入到一个内存数据结构里面,内存数据结构默认是5M
- 在shuffle的时候会有一个定时器,不定期的去估算这个内存结构的大小,当内存结构中的数据超过5M时,比如现在内存结构中的数据为5.01M,那么他会申请5.01*2-5=5.02M内存给内存数据结构。
- 如果申请成功不会进行溢写,如果申请不成功,这时候会发生溢写磁盘。
- 在溢写之前内存结构中的数据会进行排序分区
- 然后开始溢写磁盘,写磁盘是以batch的形式去写,一个batch是1万条数据,
- map task执行完成后,会将这些磁盘小文件合并成一个大的磁盘文件(有序),同时生成一个索引文件。
- reduce task去map端拉取数据的时候,首先解析索引文件,根据索引文件再去拉取对应的数据。
- 总结
- 产生磁盘小文件的个数: 2*M(map task的个数)索引文件-和磁盘文件