Spark学习笔记二之Spark Core核心概念一网打尽

Spark学习笔记二之Spark Core核心概念一网打尽

Scroll Down
	上一篇文章主要是描述了 Spark 的背景历史,也简单的介绍了一下 Spark 这门技术,这一篇正式开始Spark 的学习旅程。

​ Spark 是一个非常具有挑战性的框架。当然,首先,Spark 很”值钱“,国内大量使用 Spark 框架的公司不在少数,工资都很高,所以值得花费大量时间去深入理解它。其次,Spark 学习起来也不会很容易,各种知识点错综复杂,初入门的人可能会被这些知识点给绕晕了,所以这篇文章主要就是——敲黑板——”划重点“——期末要考的,咳咳,真实的面试中不会少了这些核心概念的部分。

​ 我阅读了诸多博客专栏书籍之后,对于Spark Core 中一些比较重要的核心概念进行了如下的归纳总结。欢迎小伙伴们来评论区和我交流交流,嘿嘿,一个人难免会有些疏忽,真诚的希望你来给我“上点眼药”。

每个核心概念可能一篇博客都讲不完,所以本文主要是介绍了这些核心概念的基础信息,方便对比和记忆。

思维导图

照例先呈上本文的思维导图。
在这里插入图片描述
在这里插入图片描述

基本数据结构

RDD(Resilient Distributed Dataset)

RDD 即弹性分布式数据集,是Spark 中最基本的数据抽象,也是整个 Spark 计算引擎的核心。它是一个只读的、带分区的数据集合,并支持多种分布式算子。

关于 RDD 的详细内容后面我会专门写几篇博客来详细解释,这里只简单介绍下几个关于 RDD 的概念。

弹性(Resilient)

具体体现在以下七个方面

  1. 数据存储内存、磁盘自动切换
  2. 基于 Lineage 的高效容错机制
  3. Task 失败重试
  4. Stage 失败重试
  5. checkpoint 和 persist,可主动或被动触发
  6. 数据调度弹性,DAGScheduler、TaskScheduler 和资源管理无关
  7. 数据分片的高度弹性

分区(Partition)

RDD 是一种分布式的数据集,由于数据量很大,因此要它被切分并存储在各个结点的分区当中。

当我们对RDD进行操作时,实际上是对每个分区中的数据并行操作。

分区实际上是为了方便集群进行并行处理。

从逻辑上来说,Partition对应的是不同数据源的split逻辑。

每个 RDD 下可以有多个 Partition,partition 是弹性分布式数据集RDD的最小单元,RDD是由分布在各个节点上的partition 组成的。

block

hdfs中的block是分布式存储的最小单元

以下是 block 和 partition 的对比。

对比blockpartition
位于存储空间计算空间
大小固定不固定
冗余设计有冗余、不会轻易丢失没有冗余设计、丢失之后重新计算得到
模块在storage模块里面所有的操作都是和block相关的在RDD里面所有的运算都是基于partition的

算子(Operation)

表示可以对 RDD 进行哪些操作

转换(Transformation)

主要作用是将一种 RDD 转换成另一种 RDD,常用的转换操作有 map,filter,groupByKey 等。

动作(Action)

主要作用是处理 RDD 得到一个或一组结果,比如将 RDD[Int]中的所有元素加起来,得到一个全局和。常见的动作操作由 reduce,count 等。

这里要注意,之所以将 RDD 的算子分成转换和动作两类,主要是因为它们的接口定义方式和执行方式是不同的。

  1. 接口定义方式:
    • 转换:RDD[A] -> RDD[B] (A,B表示数据类型)
    • 动作:RDD[A] -> B (A,B表示数据类型)
  2. 执行方式:
    • Spark程序是惰性执行(即等到绝对需要的时候才会执行计算)的,转换只会记录 RDD 的转换关系,并不会触发真正的分布式计算,而 action 才会触发程序的分布式执行。

血统(Lineage)

Transformation之后 ,RDD[A] -> RDD[B],则RDD[A] 与 RDD[B] 有血缘关系,RDD[A]称为父 RDD,RDD[B] 称为子 RDD

依赖(Dependency)

窄依赖(Narrow Dependency)

  • 父 RDD 下的 一个Partition 最多被 子 RDD 的一个 Partition 所使用

在这里插入图片描述

宽依赖Wide Dependency

父 RDD 下的一个 Partition 会被多个子 RDD 的 Partition 使用

在这里插入图片描述

共享变量(Shared Variables)

在集群多个节点之间共享的变量

广播变量(Broadcast Variables)

广播变量能够将数据以只读、序列化的方式缓存到集群中每台机器上,它使得每个 Executor 只需要保存改变量的一个副本。

广播变量有下面两种好处:

  1. 减少网络 IO 传输

  2. 减少CPU 序列化和反序列化次数

累加器(Accumulator)

累加器类似于 MapReduce 中的分布式计数器,是一个整数值,能够在各个任务中单独修改,然后自动进行汇总得到全局值。

累加器常用于追踪程序的运行状态,方便对 Spark 程序进行调试和监控。累加器可以认为是一种特殊的可变类型的广播。

管道(Pipeline)

Pipeline是一种计算思想,表示在数据处理的整个流程中,就想水从管道流过一下,是顺序执行的。Pipeline 执行机制相对来说效率更高。

关于 Pipeline 为什么效率更高详情请参考这篇文章——Spark之pipeline机制

Spark 所有的算法构造和物理执行遵循的最基本原则就是最大化 Pipeline

基于 Pipeline 的思想,数据被使用的时候才开始计算

  • 数据一直在管道中,只有在对RDD进行持久化【cache,persist...】或shuffle write时才会落地。
  • 管道中的处理也是懒加载的,只有遇到action算子之后才会执行。

在Pipeline中,我们实际上移动是计算,而不是真实的数据。为什么说移动计算比移动数据更划算?

  • 计算在数据上移动,计算作用于数据,更方便算法的构建
  • 在分布式集群中,数据一般分布在不同的节点上面,而计算一般远小于数据,移动计算远比移动数据高效

Application

Application代表的是一个独立可执行的 Spark 应用程序。

Job

通常来说,一个 Action会触发一个 Job。而一个 Application 中可能有多个 Action,故一个 Application对应了一个或者多个 Job,

Stage

每个 Job 都是由一个或者多个 Stage 构成,而后面的Stage 依赖于前面的 Stage,这就意味着,前面的 Stage 不执行完成,后面的 Stage 是不会去执行的。

结合 Pipeline 机制我们很好理解,不到真正没办法的时候,我们不想去使用网络 IO 传输,宽依赖就意味着你得从其他节点上面去拉取数据。宽依赖就是 Stage 划分的依据。

TaskSet

一个 Stage 对应一个TaskSet,TaskSet代表的是一组关联的,但相互之间没有Shuffle依赖关系的Task集合。

Task

RDD中的一个分区对应一个Task,Task是单个分区上最小的处理流程单元。一个 Stage 对应一个 或者多个Task。

ShuffleMapTask

一个 Job 中Task 对应的Stage不是最后一个,此时Stage 的计算结果还没有输出

ResultTask

一个 Job 中Task 对应的Stage就是最后一个,Stage 计算结果需要进行输出。

简单来说,Spark Job 中除了最后一个 Stage 的 Task 叫做 ResultTask,其他的都叫ShuffleMapTask

Shuffle

Shuffle继承自 MapReduce,是连接 Map和 Reduce 之间沟通数据连接的桥梁。

Shuffle是Spark 应用程序最关键的计算和数据交换环节。

结合 Stage 来看,Shuffle一般对应每个 Job 倒数第二个 Stage 和倒数第一个 Stage 之间的阶段。

结合 Task 来看,Shuffle 前的 Task 称为ShuffleMapTask,Shuffle 之后的 Task 称为ResultTask。

Shuffle Write

一批任务(ShuffleMapTask)将程序输出的临时数据写道本地磁盘

Shuffle Write一般有 3 种实现方式。

  1. Hash Based Shuffle是基于哈希实现的 Shuffle,其最大缺点是扩展性差,主要体现在两个方面:

    • 一是产生过多临时文件,而文件过多会导致两个问题:(1)写性能低下(2)操作系统资源消耗大
    • 二是写缓存区内存过大,ShuffleMapTask往磁盘写数据时,先把数据写入内存缓冲区,当缓冲区满时,才会刷新到磁盘上。默认情况下,每个文件申请32 kb 大小缓冲区,文件越多,消耗内存越大。
  2. Sort Based Shuffle 是基于排序的 Shuffle 实现。它是Spark1.2+默认的 Shuffle 实现,这里借鉴了 MapReduce 的 Shuffle 实现,但又稍有不同。

  3. Tunsten Sorted Based Shuffle算不得一个全新的shuffle 方案,它在特定场景下基于类似现有的Sort Based Shuffle处理流程,对内存/CPU/Cache使用做了非常大的优化。带来高效的同时,也就限定了自己的使用场景。如果Tungsten-sort 发现自己无法处理,则会自动使用 Sort Based Shuffle进行处理。

Shuffle Read

下一个阶段启动一批新任务(ResultTask),它们各自启动一些线程读取Shuffle Write产生的数据

Aggregate

Aggregate阶段的主要作用是Shuffle Read远程读取的 key/value 数据按照 key 将数据聚合,并调用用户自定义的聚合函数逐一处理聚集在一起的 value。

以下是MapReduce Shuffle和Spark Hash Based Shuffle和Spark Sort Based Shuffle的对比

MapReduce ShuffleSpark Hash Based ShuffleSpark 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

SparkContext通往 Spark 集群的唯一入口负责,集群进行通信、资源的申请、任务的分配和监控等,在 Spark 集群中创建 RDDs、Accumulators 和 Broadcast。

SparkContext是整个 Application 运行调度的核心。

SparkContext的核心作用是初始化 Spark应用程序运行所需要的核心组件DAGScheduler、TaskScheduler、SchedulerBackend

DAGScheduler

DAGScheduler是高层调度器,负责将整个 Job 划分成几个 Stage

TaskScheduler

TaskScheduler是底层调度器,负责对每个 Stage 的Task 进行处理

SchedulerBackend

SchedulerBackend是调度器的通信终端,它管理整个集群中当前的 Application 分配的计算资源,即Executor。当 Worker 节点中的 Executor 运行完毕 Task 后,Driver 同时负责将 SparkContext 关闭

我们通常可以直接使用SparkContext 来代表 Driver

SparkSession

SparkSession是在Spark 2.0中引入的,它使开发人员可以轻松地使用它,这样我们就不用担心不同的上下文,因为它简化了对不同上下文的访问。通过访问SparkSession,我们可以自动访问SparkContext。

SparkConf

如果用一辆车来比喻 Spark Application,那么SparkContext就是车的引擎,而SparkConf则是关于引擎的配置参数

SparkEnv

SparkEnv是Spark的执行环境对象,其中包括与众多Executor执行相关的对象。Spark 对任务的计算都依托于 Executor 的能力,所有的 Executor 都有自己的 Spark 的执行环境 SparkEnv。有了 SparkEnv,就可以将数据存储在存储体系中;就能利用计算引擎对计算任务进行处理,就可以在节点间进行通信等。

Deploying

模块

在这里插入图片描述

Driver Program

驱动程序,对应一个SparkContext

Cluster Manager

对应集群 的Master 节点,主要启动部署和管理的作用

Worker Node

对应集群的 Slave 节点

Executor

Executor就代表一个线程,它处理两种基本的业务逻辑,一种是 Driver Program,另一种是 Job 在提交之后拆分成的各个 Stage,每个 Stage 可以运行一个或者多个 Task。

ExecutorBackend

ExecutorBackend是 Executor 向集群发送更新消息的一个可插拔的接口。简单来说,ExecutorBackend就是 Executor 与集群交互的接口,这个接口在不同的调度模式下面会有不同的实现。

模式

local

本地模式,方便调试

standalone

standalone代表一个 master 和多个 slave 服务组成的 Spark 独立集群运行环境,根据 Driver 是否运行在集群上面,它分为两种情况

  1. client模式,Driver 运行在客户端,不受 master 管理和控制

  2. cluster 模式,Driver和 Executor 均运行在 slave 上

YARN

YARN模式是将Hadoop YARN 作为资源管理和调度系统,同上,根据 Driver 是否运行在集群上面,它分为两种情况

  1. yarn-client,Driver 运行在客户端,不受 YARN 管理和控制

  2. yarn-cluster,Driver 和 Executor 均运行在 YARN Container 中,受到 YARN 管理和控制

Mesos

Mesos是将 Apache Mesos 作为资源管理和调度系统

Spark Shell

Spark Shell是一种交互式 scala 解释执行器,允许用户交互式执行 scala 代码,方便调试。

持久化机制

cache

持久化到内存中

persist

存储在内存或者磁盘中,支持多种存储级别

存储级别含义解释
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

checkpoint是一种数据复用的策略,它会相对更加可靠的持久化计算结果数据,利用了 HDFS 的高可靠高容错(HDFS 集群上)

checkpoint对比 cache/persist

  • checkpoint是一个job来完成的,是执行完一个job之后,新建一个新的 job 来完成的,并不像 cache,是 job 执行过程中进行。

  • Spark 自动管理(包括创建和回收)cache 和 persist 持久化的数据,而 checkpoint 持久化的数据需要由用户自己管理

  • checkpoint 会清除 RDD 的 Lineage,避免 Lineage 过长导致序列化开销增大,而 cache 和 persist 不会清除 RDD 的 Lineage

  • checkpoint针对整个 RDD 计算链条中特别需要数据持久化的环节,进行基于 HDFS 等的数据持久化复用策略,而 cache/persist 只是存储在本地内存或者磁盘上,checkpoint 明显可靠性和容错性更高。

    对比checkpointcache/persist
    自动管理
    清除 Lineage
    存储HDFS本地内存/磁盘
    可靠性
    同一个 Job

存储子系统

在这里插入图片描述

CacheManager

RDD 在进行计算的时候,通过 CacheManager 来获取数据,并通过CacheManager来存储计算结果

BlockManager

CacheManager在进行数据读取和存储的时候,主要依赖 BlockManager 接口来实现,BlockManager决定数据从内存还是磁盘获取。BlockManager起实际的存储管控作用。

MemoryStore

保存数据到内存或者从内存中获取数据

DiskStore

保存数据磁盘或者从磁盘中获取数据

BlockManagerWorker

数据写入本地的MemoryStore或者DiskStore是一个同步操作,为了实现容错,需要将数据复制到其他计算节点,这个异步操作由BlockManagerWorker处理。

ConnetctionManager

负责与其他计算节点建立连接,并负责数据的发送和接受

BlockManagerMaster

只运行在 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的作用以及存储级别

个人博客:

CSDN