Spark 入门简介

2021-09-18  本文已影响0人  光剑书架上的书

1. 简介

Spark 的身世

Spark 是一个通用的并行计算框架,由加州伯克利大学(UC Berkeley)的 AMP 实验室开发于 2009 年,并于 2010 年开源,2013 年成长为 Apache 旗下在大数据领域最活跃的开源项目之一。
目前 Spark 的版本已经更新到了 2.4.5,并且预上线了 3.0 版本,相信未来会有更精彩的地方值得我们期待。

Spark 编程模型示意图

Spark 特性

Spark 针对 Hadoop-MR 做的改进

  1. 减少了磁盘的 I/O

Spark 将 map 端的中间输出和结果存储在内存中,避免了 reduce 端在拉取 map 端数据的时候造成大量的磁盘 I/O;并且 Spark 将应用程序上传的资源文件缓冲到了 Driver 端本地文件服务的内存中,Executor 在执行任务时直接从 Driver 的内存中读取,也节省了一部分磁盘的 I/O。

  1. 增加了并行度

由于将中间结果写到磁盘与从磁盘读取中间结果属于不同的环节,Hadoop 将它们简单地通过串行执行衔接起来。Spark 把不同的环节抽象为 Stage,允许多个 Stage 既可以串行执行,又可以并行执行。

  1. 避免重新计算

当某个 Stage 中的一个 Task 失败之后,Spark 会重新对这个 Stage 进行调度,并且会过滤掉已经执行成功的 Task,避免造成重复的计算和资源的浪费。

  1. 可选的 Shuffle 排序

MR 在 Shuffle 的时候有着固定的排序操作,但是 Spark 却可以根据不用的场景选择在 map 端排序还是在 reduce 端排序。

  1. 更加灵活的内存管理

Spark 将内|存划分为堆内存储内存、堆内执行内存、堆外存储内存和堆外执行内存。Spark 即提供了执行内存和存储内存之间固定边界的实现,也提供了执行内存和存储内存之间"软"边界的实现。Spark 默认使用第二种实现方式,无论存储或是执行内存,当哪一方的资源不足时,都可以借用另一方的资源,从而最大限度地提高了资源的利用率。

2. 运行时组件

spark-cluster-overview:

Driver

Spark 任务运行调度的核心,负责创建 SparkContext 上下文环境,内部包含 DAGScheduler、TaskScheduler、SchedulerBackend 等重要组件。

Master

Master 是在 local 和 standalone 模式部署下 Spark 集群的一名重要成员,它负责管理整个集群中所有资源的分配,接收 Worker、Driver、Application 的注册,Master 会获得所有已经注册的 Worker 节点的资源信息 (包括:ID、host、port、cpu、memory 等等),用于后续的资源分配。

Worker

组成 Spark 集群的成员之一,启动之后会主动向 Master 进行注册,负责向 Master 汇报自身所管理的资源信息,当接到 Master 的命令之后,启动相应的 Driver 或者 Executor。

Executor

负责执行 Spark 任务的容器,在 Worker 上启动,通过 launchTask() 方法创建 TaskRunner 对象来执行任务,初始化完成后会和 Driver 建立通信,并将任务最后的执行结果发送给 Driver。

3. 编程模型

Job 提交到执行过程(来源:https://blog.csdn.net/pelick/article/details/44495611

SparkContext

SparkContext 是 Spark 各种功能的主要入口点,表示和 Spark 集群的一种连接,它可以为这个集群创建 RDD、累加器、广播变量等等。

SparkConf

Spark 支持各种各样的配置参数来调整任务的运行,SparkConf 是统一管理这些配置的一个配置类,所有的配置项都由 SparkConf 来进行管理。

SparkEnv

SparkEnv 是 Spark 运行时的环境对象,其中包含了 Executor 执行任务时需要的各种对象,例如 RpcEnv、ShuffleManager、BroadcastManager、BlockManager 等,用来管理节点之间的通信、数据的 shuffle、内存空间、数据的计算存储等,所有的 Executor 都持有自己的 SparkEnv 环境对象。此外,在 local 模式下,Driver 会创建 Executor,所以在 Driver 和 CoarseGrainedExecutorBackend 进行中都有 SparkEnv 的存在。

SparkEnv 不是为了提供给外部使用的,有可能会在将来的版本变为私有。

RDD

RDD 是 Spark 的核心数据结构,全称是弹性分布式数据集( ResilientDistributed Dataset ),其本质是一种分布式的内存抽象,表示一个只读的数据分区(Partition)集合。

一个 RDD 通常只能通过其他的 RDD 转换而创建。RDD 定义了各种丰富的转换操作(如 map、join 和 filter 等),通过这些转换操作,新的 RDD 包含了如何从其他 RDD 衍生所必需的信息,这些信息构成了 RDD 之间的依赖关系( Dependency )。依赖具体分为两种,一种是窄依赖,RDD 之间分区是一一对应的;另一种是宽依赖,下游 RDD 的每个分区与上游 RDD(也称之为父 RDD)的每个分区都有关,是多对多的关系。窄依赖中的所有转换操作可以通过类似管道(Pipeline)的方式全部执行,宽依赖意味着数据需要在不同节点之间 Shuffle 传输。

RDD 计算的时候会通过一个 compute 函数得到每个分区的数据。若 RDD 是通过已有的文件系统构建的,则 compute 函数读取指定文件系统中的数据;如果 RDD 是通过其他 RDD 转换而来的,则 compute 函数执行转换逻辑,将其他 RDD 的数据进行转换。RDD 的操作算子包括两类,一类是 transformation ,用来将 RDD 进行转换,构建 RDD 的依赖关系;另一类称为 action,用来触发 RDD 的计算,得到 RDD 的相关计算结果或将 RDD 保存到文件系统中。

在 Spark 中,RDD 可以创建为对象,通过对象上的各种方法调用来对 RDD 进行转换。经过一系列的 transformation 逻辑之后,就可以调用 action 来触发 RDD 的最终计算。通常来讲,action 包括多种方式,可以是向应用程序返回结果(show、count 和 collect 等),也可以是向存储系统保存数据( saveAsTextFile 等)。在 Spark 中,只有遇到 action,才会真正地执行 RDD 的计算(注:这被称为惰性计算,英文为 Lazy Evqluation ),这样在运行时可以通过管道的方式传输多个转换。

总结而言,基于 RDD 的计算任务可描述为:从稳定的物理存储(如分布式文件系统 HDFS)中加载记录,记录被传入由一组确定性操作构成的 DAG(有向无环图),然后写回稳定存储。RDD 还可以将数据集缓存到内存中,使得在多个操作之间可以很方便地重用数据集。总的来讲,RDD 能够很方便地支持 MapReduce 应用、关系型数据处理、流式数据处理( Stream Processing )和迭代型应用(图计算、机器学习等)。

在容错性方面,基于 RDD 之间的依赖,一个任务流可以描述为 DAG。在实际执行的时候,RDD 通过 Lineage 信息(血缘关系)来完成容错,即使出现数据分区丢失,也可以通过 Lineage 信息重建分区。如果在应用程序中多次使用同一个 RDD,则可以将这个 RDD 缓存起来,该 RDD 只有在第一次计算的时候会根据 Lineage 信息得到分区的数据,在后续其他地方用到这个 RDD 的时候,会直接从缓存处读取而不用再根据 Lineage 信息计算,通过重用达到提升性能的目的。 虽然 RDD 的 Lineage 信息可以天然地实现容错(当 RDD 的某个分区数据计算失败或丢失时,可以通过 Lineage 信息重建),但是对于长时间迭代型应用来说,随着迭代的进行,RDD 与 RDD 之间的 Lineage 信息会越来越长,一旦在后续迭代过程中出错,就需要通过非常长的 Lineage 信息去重建,对性能产生很大的影响。为此,RDD 支持用 checkpoint 机制将数据保存到持久化的存储中,这样就可以切断之前的 Lineage 信息,因为 checkpoint 后的 RDD 不再需要知道它的父 RDD,可以从 checkpoint 处获取数据。

DAG

有向无环图,在 Spark 中对 RDD 的操作分为两种,一种是 transformation 的,另一种是 action 的,当不断的对 RDD 使用 transformation 算子时,会不断的生成新的 RDD,这些 RDD 之间是存在 ' 血缘关系 ' 的,因此也被称为 lineage,直到触发了 action 动作的算子之后,整个 DAG 图就结束了。DAG 的具体实现在 DAGScheduler 中。

DAGScheduler

DAGScheduler 是采用 RDD 依赖关系的逻辑计划并将其转换为实际物理计划的组件。 是一个高层次的调度器,负责将 DAG 有向无环图划分成不同的 Stage,划分的依据即为 RDD 之间的宽窄依赖,划分完成之后,构建这些 Stage 之间的父子关系,最后将每个 Stage 按照 Partition 切分成多个 Task,并且以 TaskSet 的形式提交给 TaskScheduler。

Stage

当 RDD 触发了 action 算子之后,DAGScheduler 会开始分析最终 RDD 形成的依赖关系,逆向往前推导,前一个 RDD 被看做是父 RDD。在 Spark 中 RDD 之间的依赖关系存在两种情况,一种是窄依赖 一种是宽依赖,每当遇到一个宽依赖的时候,便会以此为分界线,划分出一个 Stage

Stage 分为两种,最后一个 Stage 之前的全部是 ShuffleMapStage,最后一个 Stage 是 ResultStage。

TaskScheduler

TaskScheduler 是用于向 Worker 上的 Executor 提交任务的组件。

调度程序应用由 spark.scheduler.mode 配置参数设置的调度策略。策略有两种 FAIR(default) / FIFO

TaskScheduler 接收 DAGScheduler 提交过来的 TaskSet 集合,并向 Driver 请求分配任务运行资源,Driver 将可用的 ExecutorBackend 资源发给 TaskScheduler,TaskScheduler 将 Task 合理的分配给所有的 ExecutorBackend,最后会向 ExecutorBackend 发送 launchTask 请求,这时 Executor 会启动 TaskRunner 线程并放到线程池中执行任务。

TaskScheduler 通过 SchedulerBackend 来给 Task 分配资源,并与相应的 Executor 进行通信,让其运行任务。

Job

用户提交的一个作业,当 RDD 及其 DAG 被提交给 DAGScheduler 调度后,DAGScheduler 会将所有 RDD 中的转换及 Action 动作视为一个 Job。

一个 Job 由一到多个 Task 组成。

Task

Task 是任务真正的执行者,每个 Stage 会根据 Partition 的数量生成 Task,一个 Stage 中的 Task 最终会包装成一个 TaskSet 传给 TaskScheduler。

参考资料:

上一篇 下一篇

猜你喜欢

热点阅读