本次分享案例参考厦门大学数据库实验室
Spark基本概念和架构设计
- RDD:
Spark的核心内容就是RDD(Resillient Distributed DateSet) 弹性分布式数据集,现在基础只需要知道他是分布式内存的一个抽象概念,提供了一种高度受限的共享内存模型
这RDD里面,可以把数据从磁盘里面读出来把他封装成一个RDD,可以对RDD里面的数据进行分区,一个RDD可以有多个分区,RDD里面的分析数据,可以把他放在不同的节点上进行并行计算运行 - DAG:
是Directed Acyclic Graph(有向无环图),反映RDD之间的依赖关系 - Executor:
是运行在工作节点(WorkerNode)的一个进程,负责运行Task - Application:
用户编写的Spark的应用程序 - Task:
运行在Executor上的工作单元 - Job:
一个job包含多个RDD以及用于相应RDD上的操作 - Stage:
是Job的基本调度单位,一个Job会分为多组Task,每组Task被成为Stage,或者也被称为 TaskSet,代表了一组关联的,相互之间没有Shuffle依赖关系的任务组成的任务集
Spark运行架构
- 中间最核心的就是也就是中间的Cluster Manager ,我们也叫做集群资源管理器
- 运行作业的工作节点:WorkNode,用来负责运行作业任务的
- 每个应用的任务控制节点:Driver:负责整个应用的任务控制相当于管家
- 每个工作节点上负责具体任务的执行进程:Executor
架构设计
Spark与Hadoop MapReduce计算框架相比,Spark所采用的Executor有两个优点
可以从两个方面来讲:
- 利用多线程的方式来执行具体的任务,减少任务的启动开销,在同一个Executor里面他会同时开启多个线程来执行相关的Task,这种方式要比MapReduce,MapReduce是以进程的模型去执行,所以Spark要比MapReduce的开销小
- Executor中有一个BlockManager存储模块,会将内存和磁盘共同作为一个存储设备,很多数据RDD之类都会写入到这个BlockManager里面,如果内存足够大的时候,他会优先写内存,如果内存空间写满之后他才会把你内存中的数据溢写到磁盘中来,这样可以有效的减少磁盘的IO开销
Spark中各种概念之间的相互依赖关系
Spark中各种概念之间的相互关系
Spark运行基本流程
- 当客户端提交应用程序的时候,我们首先要为创建一个基本的应用环境,这个应用环境Driver会帮你创建一个SparkContext这个SparkContext相当于是应用程序的一个接口,并且这个SparkContext会不断的向资源管理器去申请资源,还要负责整个任务的分配和监控
- 资源管理器会为你执行任务的Executor分配资源,分配完之后会启动Executor进程,启动成功之后会不断的向资源管理器去汇报自己当前资源的使用情况
- SaprkContext会根据RDD的依赖关系构建DAG图,他会把这个DAG图提交给一个主键DAGScheduler(作业调度器),DAGScheduler会把这个图解析成Stage,每一个Stage里面都会包含多个Task,这些Task会构成一个TaskSet,DAGScheduler得到这个TaskSet之后,他会进一步提交给另一个主键TaskScheduler(任务调度器)进行调度,TaskScheduler拿到之后他要把任务分配给Executor进程去运行这些Task,当然他不会随便分配,他是接收到申请后才会进行分配,Executor会向SparkContext去申请Task,申请以后TaskScheduler把相关的Task发送给Executor去运行,并且在运行的时候会提供应用程序的代码
- Task在Executor上运行结束后会把执行结果反馈给TaskScheduler,然后反馈给DAGScheduler,整个程序运行结束之后会把数据写入,并释放所有资源
Spark运行架构的特点
- 每个Application都有自己专属的Executor进程,并且该进程在Application运行期间一直驻留。Executor进程以多线程的方式运行Task
- Spark运行过程与资源管理器无关(中间可以是Yarn之类),只要能够获取Executor进程并保持通信即可
- Task采用了数据本地性(也就是说我们在执行这个计算的时候,尽量让计算向数据靠拢,当我们要启动相关任务的时候,我们会让这个Task在靠近数据的地方去运行)和推测执行(当你要把一个Task放在数据所在节点上去运行的时候,这个数据节点资源被占光了,这个时候要不要考虑把数据迁移到其他节点去运行这个Task呢,这个时候他会去推测,如果他发现在迁移的过程中更慢,还没迁移完,被占用的资源已经被释放掉了,如果推测出迁移代价更大的话,他就不会迁移,就会等待当前占用资源的任务执行完毕,释放掉资源后自己执行Task)等优化机制
RDD的概念
- 什么是RDD:
一个RDD就是一个分布式对象集合,本质上是一个只读的分区记录集合,每个RDD可分成多个分区,每个分区就是一个数据集片段,并且一个RDD的不同分区可以被保存到集群中不同的节点上,从而可以在集群中的不同节点上进行并行计算
RDD提供了一种高度受限的共享内存模型,即RDD是只读的记录分区的集合,不能直接修改,只能基于稳定的物理存储中的数据集创建RDD,或者通过在其他RDD上执行确定的转换操作(如map、 join和group by)而创建得到新的RDD - RDD描述
RDD提供了一组丰富的操作以支持常见的数据运算,分为“动作”(Action)和“转换”(Transformation)两种类型
RDD提供的转换接口都非常简单,都是类似map、filter、 groupBy、join等粗粒度的数据转换操作,而不是针对某个数据项的细粒度修改(不适合网页爬虫)
表面上RDD的功能很受限、不够强大,实际上RDD已经被实践证明可以高效地表达许多框架的编程模型(比如MapReduce、SQL、Pregel)
Spark用Scala语言实现了RDD的API,程序员可以通过调用API实现对RDD的各种操作
RDD典型的执行过程如下:
- RDD读入外部数据源进行创建
- RDD经过一系列的转换(Transformation)操作每一次都会产生不同的RDD供给下一个转换操作使用
- 最后一个RDD经过“动作”(Action) 操作进行转换并输出到外部数据源
RDD执行过程的一个实例
这一系列处理称为一个Lineage (血缘关系),即DAG拓扑排序的结果
优点∶惰性调用、管道化、避免同步等待、不需要保存中间结果、每次操作变得简单
RDD的特性
Spark采用RDD以后能够实现高效计算的原因
现有容错机制∶数据复制或者记录日志
- RDD:血缘关系、重新计算丢失分区、无需回滚系统、重算过程在不同节点之间并行、只记录粗粒度的操作(高容错性)
- 中间结果持久化到内存,数据在内存中的多个RDD操作之间进行传递,避免了不必要的读写磁盘开销
- 存放的数据可以是Java对象,避免了不必要的对象序列化和反序列化
RDD的依赖关系和运行过程
宽窄依赖的区别
窄依赖
表现为一个父RDD的分区对应于一个子RDD的分区或多个父RDD的分区对应于一个子RDD的分区
宽依赖
为存在一个父RDD的一个分区对应一个子RDD的多个分区
Stage类型
- ShuffleMapStage:
不是最终的Stage,在它之后还有其他Stage,所以,它的输出一定需要经过Shuffle过程,并作为后续Stage的输入
这种Stage是以Shuffle为输出边界其输入边界可以是从外部获取数据,也可以是另一个ShuffleMapStage的输出其输出可以是另一个Stage的开始
在一个Job里可能有该类型的Stage,也可能没有该类型Stage - ResultStage:
最终的Stage,没有输出,而是直接产生结果或存储
这种Stage是直接输出结果,其输入边界可以是从外部获取数据,也可以是另一个ShuffleMapStage的输出
在一个Job里必定有该类型Stage
因此,一个Job含有一个或多个Stage,其中至少含有一个ResultStage
RDD运行过程
- 创建RDD对象:
从数据源当中读取相关数据,生成RDD对象 - SparkContext负责计算RDD之间的依赖关系,构建DAG
- DAGScheduler负责把DAG图分解成多个Stage,每个Stage中包含了多个Task每个Task会被TaskScheduler分发给各个WorkerNode上的Executor去执行