Spark运行架构总结
一、核心结构
Spark框架的核心是一个计算引擎,整体采用标准的master-slave结构。其中,Driver作为master,负责管理整个集群中的作业任务调度;Executor作为slave,负责实际执行任务。
二、核心组件
- Driver
- Spark驱动器节点,用于执行Spark任务中的main方法,负责实际代码的执行。
- 主要职责包括将用户程序转化为作业、在Executor之间调度任务、跟踪Executor的执行情况以及通过UI展示查询运行情况。
- Executor
- Spark Executor是集群中工作节点(Worker)中的一个JVM进程,负责在Spark作业中运行具体任务,任务彼此之间相互独立。
- 核心功能包括运行组成Spark应用的任务并将结果返回给Driver,以及通过块管理器为用户提供缓存的RDD内存式存储。
- Master & Worker
- 在Spark集群的独立部署环境中,Master负责资源的调度和分配,并进行集群的监控等职责。
- Worker运行在集群中的一台服务器上,由Master分配资源对数据进行并行的处理和计算。
- ApplicationMaster(仅在YARN环境中)
- 用于向资源调度器申请执行任务的资源容器Container,运行用户自己的程序任务job,监控整个任务的执行,跟踪整个任务的状态,处理任务失败等异常情况。
三、核心概念
- Executor与Core
- Executor是集群中专门用于计算的节点,提交应用时可以指定计算节点的个数及对应的资源(如内存大小和虚拟CPU核数量)。
- 并行度(Parallelism)
- 整个集群并行执行任务的数量,取决于框架的默认配置,应用程序也可以在运行过程中动态修改。
- 有向无环图(DAG)
- Spark程序直接映射成的数据流的高级抽象模型,用于表示程序的拓扑结构。DAG由点和线组成的拓扑图形,具有方向性且不会闭环。
四、提交流程(基于YARN环境)
- Yarn Client模式
- Driver在任务提交的本地机器上运行,适用于测试环境。
- 流程包括Driver与ResourceManager通讯申请启动ApplicationMaster,ApplicationMaster向ResourceManager申请Executor内存,Executor进程启动后向Driver反向注册,Driver开始执行main函数,之后触发Job并根据宽依赖划分stage,将task分发到各个Executor上执行。
- Yarn Cluster模式
- Driver在Yarn集群资源中执行,适用于生产环境。
- 流程与Client模式类似,但ApplicationMaster即为Driver,且在Yarn集群中启动。
五、RDD 基本定义
RDD(Resilient Distributed Dataset):弹性分布式数据集,是 Spark 中最基本的数据处理模型。它是一个抽象类,代表一个弹性的、不可变、可分区、里面的元素可并行计算的集合。
RDD 的特性
-
弹性
- 存储的弹性:内存与磁盘的自动切换。
- 容错的弹性:数据丢失可以自动恢复。
- 计算的弹性:计算出错重试机制。
- 分片的弹性:可根据需要重新分片。
-
分布式:数据存储在大数据集群不同节点上。
-
数据集:RDD 封装了计算逻辑,并不保存数据。
-
数据抽象:RDD 是一个抽象类,需要子类具体实现。
-
不可变:RDD 封装了计算逻辑,是不可以改变的,想要改变,只能产生新的 RDD,在新的 RDD 里面封装计算逻辑。
-
可分区、并行计算。
RDD 的核心属性
分区列表:用于执行任务时并行计算,是实现分布式计算的重要属性。
分区计算函数:Spark 在计算时,使用分区函数对每一个分区进行计算。
RDD 之间的依赖关系:当需求中需要将多个计算模型进行组合时,就需要将多个 RDD 建立依赖关系。
分区器(可选):当数据为 K-V 类型数据时,可以通过设定分区器自定义数据的分区。
首选位置(可选):计算数据时,可以根据计算节点的状态选择不同的节点位置进行计算。
RDD 的执行原理
Spark 框架在执行时,先申请资源,然后将应用程序的数据处理逻辑分解成一个一个的计算任务。然后将任务发到已经分配资源的计算节点上,按照指定的计算模型进行数据计算。最后得到计算结果。
六、创建Spark WordCount程序
import org.apache.spark.rdd.RDD
import org.apache.spark.{SparkConf, SparkContext}
object WordCount {
def main(args: ArrayString]): Unit = {
val sparkConf = new SparkConf().setMaster("local[*]").setAppName("WordCount")
val sc: SparkContext = new SparkContext(sparkConf)
val fileRDD: RDD[String] = sc.textFile("Spark-core/input/word.txt")
val wordRDD: RDD[String] = fileRDD.flatMap(_.split(" "))
val word2OneRDD: RDD[(String, Int)] = wordRDD.map((_, 1))
val word2CountRDD: RDD[(String, Int)] = word2OneRDD.reduceByKey(_ + _)
val word2Count: Array[(String, Int)] = word2CountRDD.collect()
word2Count.foreach(println)
sc.stop()
}
}
七、RDD的创建方式
val rdd1 = sparkContext.parallelize(List(1, 2, 3, 4))
val rdd2 = sparkContext.makeRDD(List(1, 2, 3, 4))
八、RDD并行度与分区
并行度:指能够并行计算的任务数量,可以在构建RDD时指定
分区规则:
读取内存数据时,按照并行度的设定进行分区
读取文件数据时,按照Hadoop文件读取规则进行切片分区