运行架构
核心组件
由上图可以看出,对于 Spark 框架有两个核心组件:
Driver,Spark 驱动器节点
其中Spark 驱动器节点,用于执行 Spark 任务中的 main 方法,负责实际代码的执行工作。
Driver 在 Spark 作业执行时主要负责:
将用户程序转化为作业(job)
在 Executor 之间调度任务(task)
跟踪 Executor 的执行情况
通过 UI 展示查询运行情况
简单理解,所谓的 Driver 就是驱使整个应用运行起来的程序,也称之为Driver 类。
Executor
Spark Executor 是集群中工作节点(Worker)中的一个 JVM 进程,负责在 Spark 作业中运行具体任务(Task),任务彼此之间相互独立。Spark 应用启动时,Executor 节点被同时启动,并且始终伴随着整个 Spark 应用的生命周期而存在。如果有 Executor 节点发生了故障或崩溃,Spark 应用也可以继续执行,会将出错节点上的任务调度到其他 Executor 节点上继续运行。
(任务会被重新调度到其他 executor 节点上,类似于公司中某人离职后其工作分摊给其他人。)
Executor 有两个核心功能:
负责运行组成 Spark 应用的任务,并将结果返回给驱动器进程
它们通过自身的块管理器(Block Manager)为用户程序中要求缓存的 RDD 提供内存式存储。RDD 是直接缓存在 Executor 进程内的,因此任务可以在运行时充分利用缓存数据加速运算。
( 总结: 负责运行 spark 应用的任务并将结果返回驱动器进程。
提供内存或存储缓存 RDD 数据,加速运算。)
Master 和 Worker
Spark 集群的独立部署环境中,不需要依赖其他的资源调度框架,自身就实现了资源调度的功能,所以环境中还有其他两个核心组件:
Master 和 Worker
它们两个一个类似于Yarn 环境中的 RM,一个类似于 Yarn 环境中 NM。 都是进程
Master 和 Worker 的职责:
Master 负责资源调度和分配,集群监控。
Worker 运行在集群服务器上,执行并行数据处理和计算。
ApplicationMaster
Hadoop 用户向 YARN 集群提交应用程序时,提交程序中应该包含 ApplicationMaster,用于向资源调度器申请执行任务的资源容器 Container,运行用户自己的程序任务 job,监控任务的运行状态,检查任务是否失败等异常情况。(作用)
说的简单点就是,ResourceManager(资源)和 Driver(计算)之间的解耦合靠的就是ApplicationMaster。
核心概念
并行度和有效无环图
并行度
并行度指集群并行执行任务的数量,可以在代码中动态修改。
有向无环图(DAG)
这里所谓的有向无环图,并不是真正意义的图形,而是由 Spark 程序直接映射成的数据流的高级抽象模型。简单理解就是将整个程序计算的执行过程用图形表示出来,这样更直观,更便于理解,可以用于表示程序的拓扑结构。
DAG(Directed Acyclic Graph)有向无环图是由点和线组成的拓扑图形,该图形具有方向,不会闭环。
提交流程
开发人员根据需求编写应用程序,通过 spark 客户端提交到 spark 运行环境执行。
算子:
转换算子:对数据格式内容进行转换。
行动算子:触发实际的计算操作。
RDD(弹性分布式数据集)
RDD(Resilient Distributed Dataset)是 spark 中最基本的数据处理模型.代码中是一个抽象类具有弹性、不可变、可分区、里面的元素可并行计算的集合。
弹性
存储的弹性:内存与磁盘的自动切换;
容错的弹性:数据丢失可以自动恢复;
计算的弹性:计算出错重试机制;
分片的弹性:可根据需要重新分片。
分布式
数据存储在大数据集群不同节点上
数据集
RDD封装了计算逻辑,并不保存数据
数据抽象
RDD是一个抽象类,需要子类具体实现
不可变
RDD封装了计算逻辑,是不可以改变的,想要改变,只能产生新的RDD,在新的RDD里面封装计算逻辑
可分区、并行计算
执行原理
启动 Yarn 集群环境
Spark 通过申请资源创建调度节点和计算节点
Spark 框架根据需求将计算逻辑根据分区划分成不同的任务
调度节点将任务根据计算节点状态发送到对应的计算节点进行计算
资源申请和任务分发:Spark 框架先申请资源,然后将计算逻辑分解成任务,分发到计算节点上执行。
任务执行流程:从启动环境、生成调度节点和计算节点,到分发任务、执行计算,最后得到结果。
RDD 序列化和依赖关系
序列化:RDD 序列化涉及闭包检查、序列化方法和属性(确保算子外的数据可以序列化传递到 executor 端。), Kryo 序列化框架
注意:即使使用 Kryo 序列化,也要继承 Serializable 接口。
RDD 的依赖关系:
包括血缘关系、RDD 依赖关系、窄依赖和宽依赖、RDD 阶段划分,确保数据处理过程中的容错性和可恢复性。
其中血缘关系用于恢复丢失的分区数据,窄依赖和宽依赖描述了 RDD 之间的依赖关系。
RDD 任务划分:
介绍了 RDD 任务划分的四个主要情况:application、job、stage 和 task。
Application:初始化一个 SparkContext 即生成一个 Application;
Job:一个 Action 算子就会生成一个 Job;
Stage:Stage 等于宽依赖(ShuffleDependency)的个数加 1;
Task:一个 Stage 阶段中,最后一个 RDD 的分区个数就是 Task 的个数。
注意:Application->Job->Stage->Task 每一层都是 1 对 n 的关系。
RDD 持久化:
RDD 的缓存
RDD 通过 Cache 或者 Persist 方法将前面的计算结果缓存,默认情况下会把数据以缓存在 JVM 的堆内存中。但是并不是这两个方法被调用时立即缓存,而是触发后面的 action 算子时,该 RDD 将会被缓存在计算节点的内存中,并供后面重用。
缓存有可能丢失,或者存储于内存的数据由于内存不足而被删除,RDD 的缓存容错机制保证了即使缓存丢失也能保证计算的正确执行。通过基于 RDD 的一系列转换,丢失的数据会被重算,由于 RDD 的各个 Partition 是相对独立的,因此只需要计算丢失的部分即可,并不需要重算全部 Partition。
Spark 会自动对一些 Shuffle 操作的中间数据做持久化操作(比如:reduceByKey)。这样做的目的是为了当一个节点 Shuffle 失败了避免重新计算整个输入。但是,在实际使用的时候,如果想重用数据,仍然建议调用 persist 或 cache。
RDD CheckPoint 检查点
所谓的检查点其实就是通过将 RDD 中间结果写入磁盘由于血缘依赖过长会造成容错成本过高,这样就不如在中间阶段做检查点容错,如果检查点之后有节点出现问题,可以从检查点开始重做血缘,减少了开销。
对 RDD 进行 checkpoint 操作并不会马上被执行,必须执行 Action 操作才能触发。
也就是持久化方法(cache 和 persist),这些操作在触发 action 算子后才会执行。
RDD 文件读取与保存
Spark 的数据读取及数据保存可以从两个维度来作区分:文件格式以及文件系统。
文件格式分为:text 文件、csv 文件、sequence 文件以及 Object 文件;
文件系统分为:本地文件系统、HDFS、HBASE 以及数据库。