一、概述
1、定义
Spark 是一种基于内存的快速、通用、可扩展的大数据分析计算引擎。
2、Spark和Hadoop的联系和区别
(1)联系:
(2)区别:
![](https://i-blog.csdnimg.cn/blog_migrate/19db1d5bd33dde36821971274fee8698.png)
图1 MapReduce框架处理流程
图2 Spark框架处理流程
3、核心模块
- Spark Core :Spark Core 中提供了 Spark 最基础与最核心的功能,Spark 其他的功能如:Spark SQL,Spark Streaming,GraphX, MLlib 都是在 Spark Core 的基础上进行扩展的
- Spark SQL :Spark SQL 是 Spark 用来操作结构化数据的组件。通过 Spark SQL,用户可以使用 SQL或者 Apache Hive 版本的 SQL 方言(HQL)来查询数据。
- Spark Streaming:Spark Streaming 是 Spark 平台上针对实时数据进行流式计算的组件,提供了丰富的处理数据流的 API。
- Spark MLlib:MLlib 是 Spark 提供的一个机器学习算法库。MLlib 不仅提供了模型评估、数据导入等额外的功能,还提供了一些更底层的机器学习原语。
-
Spark GraphX:GraphX 是 Spark 面向图计算提供的框架与算法库。
二、运行环境
1、Yarn模式(重点)
tar -zxvf spark-3.0.0-bin-hadoop3.2.tgz -C /opt/module
cd /opt/module
mv spark-3.0.0-bin-hadoop3.2 spark-yarn
(2)修改配置文件:
<!--是否启动一个线程检查每个任务正使用的物理内存量,如果任务超出分配值,则直接将其杀掉,默认
是 true -->
<property>
<name>yarn.nodemanager.pmem-check-enabled</name>
<value>false</value>
</property>
<!--是否启动一个线程检查每个任务正使用的虚拟内存量,如果任务超出分配值,则直接将其杀掉,默认
是 true -->
<property>
<name>yarn.nodemanager.vmem-check-enabled</name>
<value>false</value>
</property>
mv spark-env.sh.template spark-env.sh
。。。
export JAVA_HOME=/opt/module/jdk1.8.0_144
YARN_CONF_DIR=/opt/module/hadoop/etc/hadoop
bin/spark-submit \
--class org.apache.spark.examples.SparkPi \
--master yarn \
--deploy-mode cluster \
./examples/jars/spark-examples_2.12-3.0.0.jar \
10
查看 http://linux2:8088 页面,点击 History,查看历史页面
(5)配置历史服务器:
1) 修改 spark-defaults.conf.template 文件名为 spark-defaults.conf
mv spark-defaults.conf.template spark-defaults.conf
spark.eventLog.enabled true
spark.eventLog.dir hdfs://linux1:8020/directory
[root@linux1 hadoop]# sbin/start-dfs.sh
[root@linux1 hadoop]# hadoop fs -mkdir /directory
3) 修改 spark-env.sh 文件, 添加日志配置
export SPARK_HISTORY_OPTS="
-Dspark.history.ui.port=18080
-Dspark.history.fs.logDirectory=hdfs://linux1:8020/directory
-Dspark.history.retainedApplications=30"
其中,参数 1 含义:WEB UI 访问的端口号为 18080。参数 2 含义:指定历史服务器日志存储路径。 参数 3 含义:指定保存 Application 历史记录的个数,如果超过这个值,旧的应用程序信息将被删除,这个是内存中的应用数,而不是页面上显示的应用数。
4) 修改 spark-defaults.conf
spark.yarn.historyServer.address=linux1:18080
spark.history.ui.port=18080
5) 启动历史服务
sbin/start-history-server.sh
bin/spark-submit \
--class org.apache.spark.examples.SparkPi \
--master yarn \
--deploy-mode client \
./examples/jars/spark-examples_2.12-3.0.0.jar \
10
7) Web 页面查看日志:http://linux2:8088
2、local本地模式
tar -zxvf spark-3.0.0-bin-hadoop3.2.tgz -C /opt/module
cd /opt/module
mv spark-3.0.0-bin-hadoop3.2 spark-local
(2)启动Local环境:
bin/spark-shell
2) 启动成功后,可以输入网址进行 Web UI 监控页面访问
http://虚拟机地址:4040
(3)命令行工具:
sc.textFile("data/word.txt").flatMap(_.split("")).map((_,1)).reduceByKey(_+_).collect
(4)退出本地模式:
:quit
(5)提交应用:
bin/spark-submit \
--class org.apache.spark.examples.SparkPi \
--master local[2] \
./examples/jars/spark-examples_2.12-3.0.0.jar \
10
![](https://i-blog.csdnimg.cn/blog_migrate/a3b3ac7e76636783ecb0b28e2b99ecf4.png)
3、standalone模式
只使用 Spark 自身节点运行的集群模式,也就是我们所谓的 独立部署(Standalone)模式。Spark 的 Standalone 模式体现了经典的 master-slave 模式。
![](https://i-blog.csdnimg.cn/blog_migrate/f2437ac3cd741b4ef2c0d1ad2556987d.png)
分为七个阶段:解压缩文件、修改配置文件、启动集群、提交应用、提交参数说明、配置历史服务、配置高可用
三、运行架构(重点)
1、核心组件
(1)Driver
-
将用户程序转化为作业( job )
-
通过 UI 展示查询运行情况
-
跟踪 Executor 的执行情况
-
在 Executor 之间调度任务 (task)
(2)Executor
Spark Executor 是集群中工作节点(Worker)中的一个 JVM 进程,负责在 Spark 作业 中运行具体任务(Task),任务彼此之间相互独立。Spark 应用启动时,Executor 节点被同时启动,并且始终伴随着整个 Spark 应用的生命周期而存在。如果有 Executor节点发生了 故障或崩溃,Spark 应用也可以继续执行,会将出错节点上的任务调度到其他 Executor 节点上继续运行。
Executor 有两个核心功能:
- 负责运行组成 Spark 应用的任务,并将结果返回给驱动器进程
-
它们通过自身的块管理器( Block Manager )为用户程序中要求缓存的 RDD 提供内存式存储。 RDD 是直接缓存在 Executor 进程内的,因此任务可以在运行时充分利用缓存数据加速运算。
(3)Master&Worker
Spark 集群的独立部署环境中,不需要依赖其他的资源调度框架,自身就实现了资源调度的功能,所以环境中还有其他两个核心组件:Master 和 Worker,这里的 Master 是一个进程,主要负责资源的调度和分配,并进行集群的监控等职责,类似于 Yarn 环境中的 RM, 而 Worker呢,也是进程,一个 Worker 运行在集群中的一台服务器上,由 Master分配资源对数据进行并行的处理和计算,类似于Yarn 环境中 NM。
(4)ApplicationMaster
2、核心概念
(1)Executor与Core
![](https://i-blog.csdnimg.cn/blog_migrate/227bc306e68de8d914c51672609e43c9.png)
(2)并行度
在分布式计算框架中一般都是多个任务同时执行,由于任务分布在不同的计算节点进行 计算,所以能够真正地实现多任务并行执行,记住,这里是并行,而不是并发。这里我们将 整个集群并行执行任务的数量称之为并行度。那么一个作业到底并行度是多少呢?这个取决于框架的默认配置。应用程序也可以在运行过程中动态修改。
(3)有向无环图
3、提交流程
![](https://i-blog.csdnimg.cn/blog_migrate/b7c5383cc10e9513dc68674f598033d4.png)
图3 提交具体细节
- 在 YARN Cluster 模式下,任务提交后会和 ResourceManager 通讯申请启动 ApplicationMaster,
- 随后 ResourceManager 分配 container,在合适的 NodeManager 上启动ApplicationMaster, 此时的 ApplicationMaster 就是 Driver。
- Driver 启动后向 ResourceManager 申请 Executor 内存,ResourceManager 接到 ApplicationMaster 的资源申请后会分配 container,然后在合适的 NodeManager 上启动 Executor 进程
- Executor 进程启动后会向 Driver 反向注册,Executor 全部注册完成后 Driver 开始执行 main 函数,
- 之后执行到 Action 算子时,触发一个 Job,并根据宽依赖开始划分 stage,每个 stage 生成对应的 TaskSet,之后将 task 分发到各个 Executor 上执行。
四、核心编程
- RDD : 弹性分布式数据集
- 累加器:分布式共享只写变量
- 广播变量:分布式共享只读变量
1、RDD(重点)
(1)IO操作和shuffle
![](https://i-blog.csdnimg.cn/blog_migrate/f6a9cec443d9c65e8a82887ed913d4ee.png)
图4 IO操作
![](https://i-blog.csdnimg.cn/blog_migrate/687cefa61d7a6533178ca661fe272075.png)
(2)RDD数据处理方式
![](https://i-blog.csdnimg.cn/blog_migrate/cb2c1d4bae6671feaf3466bde74f1eb4.png)
图6 RDD数据处理方式
RDD的特点包括:
-
弹性:存储的弹性:内存与磁盘的自动切换; 容错的弹性:数据丢失可以自动恢复;计算的弹性:计算出错重试机制;分片的弹性:可根据需要重新分片
-
分布式:数据存储在大数据集群不同节点上
-
数据抽象: RDD 是一个抽象类,需要子类具体实现
-
可分区、并行计算
-
不可变: RDD 封装了计算逻辑,是不可以改变的,想要改变,只能产生新的 RDD ,在新的 RDD 里面封装计算逻辑
-
数据集: RDD 封装了计算逻辑,并不保存数据
(3)RDD核心属性(5个)
- 分区列表:RDD 数据结构中存在分区列表,用于执行任务时并行计算,是实现分布式计算的重要属性。数据之间无关联
- 分区计算函数:Spark 在计算时,是使用分区函数对每一个分区进行计算。计算逻辑相同
- RDD之间的依赖关系:RDD 是计算模型的封装,当需求中需要将多个计算模型进行组合时,就需要将多个 RDD 建立依赖关系
- 分区器(可选):当数据为 KV 类型数据时,可以通过设定分区器自定义数据的分区
- 首选位置(可选):计算数据时,可以根据计算节点的状态选择不同的节点位置进行计算。判断计算发送到哪个节点效率最优,移动数据不如移动计算
(4)执行原理
![](https://i-blog.csdnimg.cn/blog_migrate/5775f758c541af0eddbb4dc9085b8bc7.png)
2) Spark 通过申请资源创建调度节点和计算节点
![](https://i-blog.csdnimg.cn/blog_migrate/18242a69b9abe17d32487d4026ab621c.png)
3) Spark 框架根据需求将计算逻辑根据分区划分成不同的任务
![](https://i-blog.csdnimg.cn/blog_migrate/100e09d7b0a8a7275645999f2c9f387e.png)
4) 调度节点将任务根据计算节点状态发送到对应的计算节点进行计算
![](https://i-blog.csdnimg.cn/blog_migrate/9dcaf02ee1667ccbe391b041e950de24.png)
(5)基础编程
- 从集合(内存)中创建 RDD:从集合中创建 RDD,Spark 主要提供了两个方法:parallelize 和makeRDD。从底层代码实现来讲,makeRDD 方法其实就是 parallelize 方法
- 从外部存储(文件)创建 RDD:由外部存储系统的数据集创建RDD包括:本地的文件系统,所有 Hadoop 支持的数据集,比如 HDFS、HBase等
- 从其他RDD 创建:主要是通过一个 RDD 运算完后,再产生新的 RDD
- 直接创建RDD(new):使用new 的方式直接构造RDD,一般由Spark框架自身使用
- 读取内存数据时,数据可以按照并行度的设定进行数据的分区操作
- 读取文件数据时,数据是按照 Hadoop 文件读取的规则进行切片分区,而切片规则和数据读取的规则有些差异
3)RDD 转换算子
RDD转换算子是功能的补充和封装,将旧的RDD包装成新的RDD(map,flatMap)RDD根据数据处理方式的不同将算子整体上分为Value类型、双Value类型(指双源)和Key-Value类型
- Value类型:map、mapPartitions、mapPartitionsWithIndex、flatMap、glom、groupBy、filter、sample、distinct、coalesce、reparation、sortBy等
- 双Value类型:intersection、union、subtract、zip
- Key-Value类型:partitionBy、reduceByKey、groupByKey、aggregateByKey、foldByKey、combineByKey、sortByKey、join、leftOuterJoin、cogroup等
其中reduceByKey和groupByKey的区别:
-
从 shuffle 的角度 : reduceByKey 和 groupByKey 都存在 shuffle 的操作,但是 reduceByKey可以在 shuffle 前对分区内相同 key 的数据进行预聚合( combine )功能,这样会减少落盘的数据量,而 groupByKey 只是进行分组,不存在数据量减少的问题, reduceByKey 性能比高。
-
从功能的角度 : reduceByKey 其实包含分组和聚合的功能。 GroupByKey 只能分组,不能聚合,所以在分组聚合的场合下,推荐使用 reduceByKey ,如果仅仅是分组而不需要聚合。那么还是只能使用 groupByKey
![](https://i-blog.csdnimg.cn/blog_migrate/913e9582b45257a8a30a40c28c958c47.png)
4)行动算子
行动算子是触发任务和作业的执行(collect)
reduce、collect、count、first、take、takeOrdered、aggregate、countByKey、save相关算子、fold、foreach
5)RDD序列化
闭包检查
从计算的角度, 算子以外的代码都是在 Driver 端执行, 算子里面的代码都是在 Executor 端执行。那么在 scala 的函数式编程中,就会导致算子内经常会用到算子外的数据,这样就 形成了闭包的效果,如果使用的算子外的数据无法序列化,就意味着无法传值给 Executor 端执行,就会发生错误,所以需要在执行任务计算前,检测闭包内的对象是否可以进行序列化,这个操作我们称之为闭包检测。
序列化属性
从计算的角度, 算子以外的代码都是在 Driver 端执行, 算子里面的代码都是在 Executor端执行
6)RDD依赖关系
a:RDD依赖关系
这里所谓的依赖关系,其实就是两个相邻 RDD之间的关系
b:RDD血缘关系
RDD血缘关系是指多个连续的RDD的依赖关系。因为RDD不保存数据,所以RDD为了提高容错性,需要将RDD之间的关系保存下来。一旦出现错误,可以根据血缘关系将数据源重新读取进行计算。
c:RDD窄依赖
窄依赖表示每一个父(上游)RDD 的 Partition 最多被子(下游)RDD 的一个 Partition 使用, 窄依赖我们形象的比喻为独生子女。新的RDD的一个分区的数据依赖于旧的一个RDD分区的数据,这个依赖称为OneToOne依赖
d:RDD宽依赖
![](https://i-blog.csdnimg.cn/blog_migrate/2e925d15269e70c71c8fe898c7cce767.png)
阶段和任务划分
DAG(Directed Acyclic Graph)有向无环图是由点和线组成的拓扑图形,该图形具有方向,不会闭环。例如,DAG 记录了 RDD 的转换过程和任务的阶段。
任务划分中分为:Application、Job、Stage 和 Task
- Application:初始化一个 SparkContext 即生成一个 Application;
- Job:一个 Action 算子就会生成一个 Job;
- Stage:Stage 等于宽依赖(ShuffleDependency)的个数加 1。当RDD中存在shuffle依赖时,阶段会自动加1:阶段数量=shuffle的依赖数量+1
- Task:一个 Stage 阶段中,最后一个 RDD(ResultStage只有一个,最后需要执行的阶段)的分区个数就是 Task 的个数。 任务的数量= 当前阶段中最后一个RDD的分区数量。
阶段和任务划分具体图:
ShuffleMapStage=>ShuffleMapTask ResultStage=>ResultTask
7)RDD持久化
上图的问题是最后会出错,无法实现。因为RDD不存储数据,如果一个RDD需要重复使用,那么需要重新从头再次执行来获取数据。RDD对象可以重用,但是数据无法重用
RDD对象的持久化操作不一定是为了重用。在数据执行较长时,或数据比较重要的场合也可可以采用持久化操作。
缓存、检查点和persist的区别:
- RDD Cache缓存:将数据临时存储在内存中进行数据重用;会在血缘关系中添加新的依赖,一旦出现问题,可以重头读取数据
- CheckPoint检查点:将数据长久地保存在磁盘文件中进行数据重用;涉及到磁盘IO,性能较低,但数据安全;为了保证数据安全,所以一般情况下,会独立执行作业;为了能够提高效率,一般情况下,是需要和Cache联合使用;执行过程中,会切断血缘关系。重新建立新的血缘关系;checkpoint等同于改变数据源
- persist:将数据临时存储在磁盘文件中进行数据重用;涉及到磁盘IO,性能较低,但数据安全;如果作业执行完毕,临时保存的数据文件将会丢失
8)RDD 分区器
Spark 目前支持 Hash 分区和 Range 分区,和用户自定义分区。Hash 分区为当前的默认 分区。分区器直接决定了 RDD 中分区的个数、RDD 中每条数据经过 Shuffle 后进入哪个分区,进而决定了 Reduce 的个数。
- 只有 Key-Value 类型的 RDD 才有分区器,非 Key-Value 类型的 RDD 分区的值是 None
- 每个 RDD 的分区 ID 范围:0 ~ (numPartitions - 1),决定这个值是属于那个分区的。
Hash 分区:对于给定的 key,计算其 hashCode,并除以分区个数取余
Range 分区:将一定范围内的数据映射到一个分区中,尽量保证每个分区数据均匀,而且分区间有序
9)RDD 文件读取与保存
Spark 的数据读取及数据保存可以从两个维度来作区分:文件格式以及文件系统。
- 文件格式分为:text 文件、csv 文件、sequence 文件以及 Object 文件;
- 文件系统分为:本地文件系统、HDFS、HBASE 以及数据库。
2、累加器
3、广播变量
![](https://i-blog.csdnimg.cn/blog_migrate/f8ff05a94ae25dec3905aa6e37d6145c.png)
闭包数据,都是以Task为单位发送的,每个任务中包含闭包数据这样可能会导致,一个Executor包含大量重复的数据,一个Executor中含有大量重复的数据,并且占有大量的内存。
Executor其实是一个JVM,所以在启动时,会自动分配内存完全可以将任务中的闭包数据放置在Executor的内存中,达到共享的目的。
Spark中的广播变量就可以将闭包的数据保存到Executor的内存中。
Spark中的广播变量不能够更改:分布式共享只读变量