上一篇文章主要是描述了 Spark 的背景历史,也简单的介绍了一下 Spark 这门技术,这一篇正式开始Spark 的学习旅程。
Spark 是一个非常具有挑战性的框架。当然,首先,Spark 很”值钱“,国内大量使用 Spark 框架的公司不在少数,工资都很高,所以值得花费大量时间去深入理解它。其次,Spark 学习起来也不会很容易,各种知识点错综复杂,初入门的人可能会被这些知识点给绕晕了,所以这篇文章主要就是——敲黑板——”划重点“——期末要考的,咳咳,真实的面试中不会少了这些核心概念的部分。
我阅读了诸多博客专栏书籍之后,对于Spark Core 中一些比较重要的核心概念进行了如下的归纳总结。欢迎小伙伴们来评论区和我交流交流,嘿嘿,一个人难免会有些疏忽,真诚的希望你来给我“上点眼药”。
每个核心概念可能一篇博客都讲不完,所以本文主要是介绍了这些核心概念的基础信息,方便对比和记忆。
RDD 即弹性分布式数据集,是Spark 中最基本的数据抽象,也是整个 Spark 计算引擎的核心。它是一个只读的、带分区的数据集合,并支持多种分布式算子。
关于 RDD 的详细内容后面我会专门写几篇博客来详细解释,这里只简单介绍下几个关于 RDD 的概念。
具体体现在以下七个方面
RDD 是一种分布式的数据集,由于数据量很大,因此要它被切分并存储在各个结点的分区当中。
当我们对RDD进行操作时,实际上是对每个分区中的数据并行操作。
分区实际上是为了方便集群进行并行处理。
从逻辑上来说,Partition对应的是不同数据源的split逻辑。
每个 RDD 下可以有多个 Partition,partition 是弹性分布式数据集RDD的最小单元,RDD是由分布在各个节点上的partition 组成的。
hdfs中的block是分布式存储的最小单元
以下是 block 和 partition 的对比。
对比 | block | partition |
---|---|---|
位于 | 存储空间 | 计算空间 |
大小 | 固定 | 不固定 |
冗余设计 | 有冗余、不会轻易丢失 | 没有冗余设计、丢失之后重新计算得到 |
模块 | 在storage模块里面所有的操作都是和block相关的 | 在RDD里面所有的运算都是基于partition的 |
表示可以对 RDD 进行哪些操作
主要作用是将一种 RDD 转换成另一种 RDD,常用的转换操作有 map,filter,groupByKey 等。
主要作用是处理 RDD 得到一个或一组结果,比如将 RDD[Int]中的所有元素加起来,得到一个全局和。常见的动作操作由 reduce,count 等。
这里要注意,之所以将 RDD 的算子分成转换和动作两类,主要是因为它们的接口定义方式和执行方式是不同的。
Transformation之后 ,RDD[A] -> RDD[B],则RDD[A] 与 RDD[B] 有血缘关系,RDD[A]称为父 RDD,RDD[B] 称为子 RDD
父 RDD 下的一个 Partition 会被多个子 RDD 的 Partition 使用
在集群多个节点之间共享的变量
广播变量能够将数据以只读、序列化的方式缓存到集群中每台机器上,它使得每个 Executor 只需要保存改变量的一个副本。
广播变量有下面两种好处:
减少网络 IO 传输
减少CPU 序列化和反序列化次数
累加器类似于 MapReduce 中的分布式计数器,是一个整数值,能够在各个任务中单独修改,然后自动进行汇总得到全局值。
累加器常用于追踪程序的运行状态,方便对 Spark 程序进行调试和监控。累加器可以认为是一种特殊的可变类型的广播。
Pipeline是一种计算思想,表示在数据处理的整个流程中,就想水从管道流过一下,是顺序执行的。Pipeline 执行机制相对来说效率更高。
关于 Pipeline 为什么效率更高详情请参考这篇文章——Spark之pipeline机制
Spark 所有的算法构造和物理执行遵循的最基本原则就是最大化 Pipeline。
基于 Pipeline 的思想,数据被使用的时候才开始计算
在Pipeline中,我们实际上移动是计算,而不是真实的数据。为什么说移动计算比移动数据更划算?
Application代表的是一个独立可执行的 Spark 应用程序。
通常来说,一个 Action会触发一个 Job。而一个 Application 中可能有多个 Action,故一个 Application对应了一个或者多个 Job,
每个 Job 都是由一个或者多个 Stage 构成,而后面的Stage 依赖于前面的 Stage,这就意味着,前面的 Stage 不执行完成,后面的 Stage 是不会去执行的。
结合 Pipeline 机制我们很好理解,不到真正没办法的时候,我们不想去使用网络 IO 传输,宽依赖就意味着你得从其他节点上面去拉取数据。宽依赖就是 Stage 划分的依据。
一个 Stage 对应一个TaskSet,TaskSet代表的是一组关联的,但相互之间没有Shuffle依赖关系的Task集合。
RDD中的一个分区对应一个Task,Task是单个分区上最小的处理流程单元。一个 Stage 对应一个 或者多个Task。
一个 Job 中Task 对应的Stage不是最后一个,此时Stage 的计算结果还没有输出
一个 Job 中Task 对应的Stage就是最后一个,Stage 计算结果需要进行输出。
简单来说,Spark Job 中除了最后一个 Stage 的 Task 叫做 ResultTask,其他的都叫ShuffleMapTask
Shuffle继承自 MapReduce,是连接 Map和 Reduce 之间沟通数据连接的桥梁。
Shuffle是Spark 应用程序最关键的计算和数据交换环节。
结合 Stage 来看,Shuffle一般对应每个 Job 倒数第二个 Stage 和倒数第一个 Stage 之间的阶段。
结合 Task 来看,Shuffle 前的 Task 称为ShuffleMapTask,Shuffle 之后的 Task 称为ResultTask。
一批任务(ShuffleMapTask)将程序输出的临时数据写道本地磁盘
Shuffle Write一般有 3 种实现方式。
Hash Based Shuffle是基于哈希实现的 Shuffle,其最大缺点是扩展性差,主要体现在两个方面:
Sort Based Shuffle 是基于排序的 Shuffle 实现。它是Spark1.2+默认的 Shuffle 实现,这里借鉴了 MapReduce 的 Shuffle 实现,但又稍有不同。
Tunsten Sorted Based Shuffle算不得一个全新的shuffle 方案,它在特定场景下基于类似现有的Sort Based Shuffle处理流程,对内存/CPU/Cache使用做了非常大的优化。带来高效的同时,也就限定了自己的使用场景。如果Tungsten-sort 发现自己无法处理,则会自动使用 Sort Based Shuffle进行处理。
下一个阶段启动一批新任务(ResultTask),它们各自启动一些线程读取Shuffle Write产生的数据
Aggregate阶段的主要作用是Shuffle Read远程读取的 key/value 数据按照 key 将数据聚合,并调用用户自定义的聚合函数逐一处理聚集在一起的 value。
以下是MapReduce Shuffle和Spark Hash Based Shuffle和Spark Sort Based Shuffle的对比
MapReduce Shuffle | Spark Hash Based Shuffle | Spark Sort Based Shuffle | ||
---|---|---|---|---|
Shuffle Write | 是否需要排序 | 按照 partition 编号和 key 两个关键字排序 | 不排序 | 依照 partition 编号排序 |
Shuffle Write | 生成文件数目 | M 个任务,每个生成一个数据文件和一个索引文件,共M*2 个文件 | 每个 core 生成 R 个文件,总共 C*R 个文件(C 代表 core 的个数) | 与MapReduce Shuffle一致 |
Shuffle Read | / | 通过 HTTP 多线程拷贝数据 | 通过 HTTP 多线程拷贝数据 | 通过 HTTP 多线程拷贝数据 |
Aggregate | 是否排序 | 是 | 是 | 是 |
Aggregate | 如何聚集 key | 归并排序 | 哈希表+归并排序 | 哈希表+归并排序 |
SparkContext通往 Spark 集群的唯一入口负责,集群进行通信、资源的申请、任务的分配和监控等,在 Spark 集群中创建 RDDs、Accumulators 和 Broadcast。
SparkContext是整个 Application 运行调度的核心。
SparkContext的核心作用是初始化 Spark应用程序运行所需要的核心组件DAGScheduler、TaskScheduler、SchedulerBackend
DAGScheduler是高层调度器,负责将整个 Job 划分成几个 Stage
TaskScheduler是底层调度器,负责对每个 Stage 的Task 进行处理
SchedulerBackend是调度器的通信终端,它管理整个集群中当前的 Application 分配的计算资源,即Executor。当 Worker 节点中的 Executor 运行完毕 Task 后,Driver 同时负责将 SparkContext 关闭
我们通常可以直接使用SparkContext 来代表 Driver
SparkSession是在Spark 2.0中引入的,它使开发人员可以轻松地使用它,这样我们就不用担心不同的上下文,因为它简化了对不同上下文的访问。通过访问SparkSession,我们可以自动访问SparkContext。
如果用一辆车来比喻 Spark Application,那么SparkContext就是车的引擎,而SparkConf则是关于引擎的配置参数
SparkEnv是Spark的执行环境对象,其中包括与众多Executor执行相关的对象。Spark 对任务的计算都依托于 Executor 的能力,所有的 Executor 都有自己的 Spark 的执行环境 SparkEnv。有了 SparkEnv,就可以将数据存储在存储体系中;就能利用计算引擎对计算任务进行处理,就可以在节点间进行通信等。
驱动程序,对应一个SparkContext
对应集群 的Master 节点,主要启动部署和管理的作用
对应集群的 Slave 节点
Executor就代表一个线程,它处理两种基本的业务逻辑,一种是 Driver Program,另一种是 Job 在提交之后拆分成的各个 Stage,每个 Stage 可以运行一个或者多个 Task。
ExecutorBackend是 Executor 向集群发送更新消息的一个可插拔的接口。简单来说,ExecutorBackend就是 Executor 与集群交互的接口,这个接口在不同的调度模式下面会有不同的实现。
本地模式,方便调试
standalone代表一个 master 和多个 slave 服务组成的 Spark 独立集群运行环境,根据 Driver 是否运行在集群上面,它分为两种情况
client模式,Driver 运行在客户端,不受 master 管理和控制
cluster 模式,Driver和 Executor 均运行在 slave 上
YARN模式是将Hadoop YARN 作为资源管理和调度系统,同上,根据 Driver 是否运行在集群上面,它分为两种情况
yarn-client,Driver 运行在客户端,不受 YARN 管理和控制
yarn-cluster,Driver 和 Executor 均运行在 YARN Container 中,受到 YARN 管理和控制
Mesos是将 Apache Mesos 作为资源管理和调度系统
Spark Shell是一种交互式 scala 解释执行器,允许用户交互式执行 scala 代码,方便调试。
持久化到内存中
存储在内存或者磁盘中,支持多种存储级别
存储级别 | 含义解释 |
---|---|
MEMORY_ONLY | 使用未序列化的Java对象格式,将数据保存在内存中。如果内存不够存放所有的数据,则数据可能就不会进行持久化。那么下次对这个RDD执行算子操作时,那些没有被持久化的数据,需要从源头处重新计算一遍。这是默认的持久化策略,使用cache()方法时,实际就是使用的这种持久化策略。 |
MEMORY_AND_DISK | 使用未序列化的Java对象格式,优先尝试将数据保存在内存中。如果内存不够存放所有的数据,会将数据写入磁盘文件中,下次对这个RDD执行算子时,持久化在磁盘文件中的数据会被读取出来使用。 |
MEMORY_ONLY_SER | 基本含义同MEMORY_ONLY。唯一的区别是,会将RDD中的数据进行序列化,RDD的每个partition会被序列化成一个字节数组。这种方式更加节省内存,从而可以避免持久化的数据占用过多内存导致频繁GC。 |
MEMORY_AND_DISK_SER | 基本含义同MEMORY_AND_DISK。唯一的区别是,会将RDD中的数据进行序列化,RDD的每个partition会被序列化成一个字节数组。这种方式更加节省内存,从而可以避免持久化的数据占用过多内存导致频繁GC。 |
DISK_ONLY | 使用未序列化的Java对象格式,将数据全部写入磁盘文件中。 |
MEMORY_ONLY_2, MEMORY_AND_DISK_2 | 对于上述任意一种持久化策略,如果加上后缀_2,代表的是将每个持久化的数据,都复制一份副本,并将副本保存到其他节点上。这种基于副本的持久化机制主要用于进行容错。假如某个节点挂掉,节点的内存或磁盘中的持久化数据丢失了,那么后续对RDD计算时还可以使用该数据在其他节点上的副本。如果没有副本的话,就只能将这些数据从源头处重新计算一遍了。 |
checkpoint是一种数据复用的策略,它会相对更加可靠的持久化计算结果数据,利用了 HDFS 的高可靠高容错(HDFS 集群上)
checkpoint是一个job来完成的,是执行完一个job之后,新建一个新的 job 来完成的,并不像 cache,是 job 执行过程中进行。
Spark 自动管理(包括创建和回收)cache 和 persist 持久化的数据,而 checkpoint 持久化的数据需要由用户自己管理
checkpoint 会清除 RDD 的 Lineage,避免 Lineage 过长导致序列化开销增大,而 cache 和 persist 不会清除 RDD 的 Lineage
checkpoint针对整个 RDD 计算链条中特别需要数据持久化的环节,进行基于 HDFS 等的数据持久化复用策略,而 cache/persist 只是存储在本地内存或者磁盘上,checkpoint 明显可靠性和容错性更高。
对比 | checkpoint | cache/persist |
---|---|---|
自动管理 | 是 | 否 |
清除 Lineage | 是 | 否 |
存储 | HDFS | 本地内存/磁盘 |
可靠性 | 高 | 低 |
同一个 Job | 否 | 是 |
RDD 在进行计算的时候,通过 CacheManager 来获取数据,并通过CacheManager来存储计算结果
CacheManager在进行数据读取和存储的时候,主要依赖 BlockManager 接口来实现,BlockManager决定数据从内存还是磁盘获取。BlockManager起实际的存储管控作用。
保存数据到内存或者从内存中获取数据
保存数据磁盘或者从磁盘中获取数据
数据写入本地的MemoryStore或者DiskStore是一个同步操作,为了实现容错,需要将数据复制到其他计算节点,这个异步操作由BlockManagerWorker处理。
负责与其他计算节点建立连接,并负责数据的发送和接受
只运行在 Driver Program 所在的 Executor 上,功能是负责记录所有 Block Ids 存储在哪个 Slaver 节点上
参考:
《Spark:权威指南》——Bill Chambers / Matei Zaharia著
《Spark 大数据商业实战三部曲:内核解密|商业案例|性能调优》 -王家林/段智华/夏阳 著
《大数据架构详解-从数据获取到深度学习》-朱洁/罗华霖著
《大数据技术体系详解-原理、架构与实践》-董西成著
《从 0 开始学大数据》-李智慧
《大规模数据处理实战》 -蔡元楠
Spark Tungsten-sort Based Shuffle 分析
Spark中的partition和block的关系
Spark 中容错( checkpoint )和持久化( cache )的异同
Spark中cache和persist的作用以及存储级别
个人网站: https://www.shockang.com