0%

【循序渐进学 Spark】理论教学

这本书在理论方面还可以,比较深入探讨了 Spark 相关的方方面面,但有点过于理论化。


更新历史

  • 2020.02.18:重新上线
  • 2019.05.24:完成阅读与读后感
  • 2019.05.23:开始阅读

读后感

这本书在理论方面还可以,比较深入探讨了 Spark 相关的方方面面,但有点过于理论化。换句话说就是没有实际的上手例子,就是把文档翻译和整理一起。不过对于新人来说,看完还是没有任何对 spark 的概念,因为没有实战。

阅读笔记

第 1 章 Spark 架构与集群环境

  • Spark 采用 Akka 来启动任务,通过线程池复用线程来避免线程启动及切换产生的开销
  • 整个 Spark 生态称为伯克利数据分析栈 BDAS

  • Spark 架构采用了分布式计算中的 Master-Slave 模型

Spark 架构中的组件:

  1. Client:提交应用的客户端
  2. Driver:执行 Application 中的 main 函数并创建 SparkContext
  3. ClusterManager:在 YARN 模式中为资源管理器。在 Standalone 模式中为 Master,控制整个集群
  4. Worker:从节点,负责控制计算节点。启动 Executor 或 Driver,在 YARN 模式中为 NodeManager
  5. Executor:在计算节点上执行任务的组件
  6. SparkContext:应用的上下文,控制应用的生命周期
  7. RDD:弹性分布式数据集,Spark 的基本计算单元,一组 RDD 可形成有向无环图
  8. DAG Scheduler:根据应用构建基于 Stage 的 DAG,并将 Stage 提交给 Task Scheduler
  9. Task Scheduler:将 Task 分发给 Executor 执行
  10. SparkConf:线程级别的上下文,存储运行时重要组件的应用,具体如下:
    1. SparkConf:存储配置信息
    2. BroadcastManager:负责广播变量的控制及元信息的存储
    3. BlockManager:负责 Block 的管理、创建和查找
    4. MetricsSystem:监控运行时的性能指标
    5. MapOutputTracker:负责 shuffle 元信息的存储

Spark 的具体流程:

  1. 用户在 Client 提交了应用
  2. Master 找到 Worker,并启动 Driver
  3. Driver 向资源管理器(YARN 模式)或者 Master(Standalone 模式)申请资源,并将应用转化为 RDD Graph
  4. DAG Scheduler 将 RDD Graph 转化为 Stage 的有向无环图提交给 Task Scheduler
  5. Task Scheduler 提交任务给 Executor 执行

Intellij 配置

创建项目后要在

  • Project Structure -> Libraries 单击 + 号,选择 java,定位到 Spark 根目录下的 lib 目录,选中 spark-assembly-xxxx.jar
  • 再单击 + 号,选择 scala,选中 scala 安装目录,就会自动引用相关库
  • Project Structure -> Platform Settings -> SDKs,单击 + 号,选择 JDK,然后定位到 JDK 安装目录

第 2 章 Spark 编程模型

Spark 依靠 Scala 强大的函数式编程 Actor 通信模式、闭包、容器、泛型,并借助统一资源调度框架,成为一个简洁、高效、强大的分布式大数据处理框架。

RDD

RDD(Resilient Distributed Datasets, 弹性分布式数据集)是一个容错的、并行的数据结构,可以让用户显式地将数据存储到磁盘或内存中,并控制数据的分区。

  • RDD 的两种创建方式
    • 从文件系统输入创建
    • 从已存在的 RDD 转换得到新的 RDD
  • RDD 的两种操作算子
    • Transformation 变换:延迟执行,直到 Action 操作触发,才真正执行
    • Action 行动:触发 Spark 提交作业,并将数据输出到 Spark 系统

RDD 从直观上可以看作一个数组,本质上是逻辑分区记录的集合。在集群中,一个 RDD 可以包含多个分布在不同节点上的分区,每个分区是一个 dataset 片段。

RDD 可以相互依赖,如果 RDD 的每个分区最多只能被一个 Child RDD 的一个分区使用,就是窄依赖(narrow dependency),若有多个则为宽依赖(wide dependency)。不同的操作会产生不同的依赖,比如 map 操作产生窄依赖,join 操作产生宽依赖。

RDD 特性总结:

  1. RDD 是不变的 immutable 数据结构存储
  2. RDD 将数据存储在内存中,从而提供了低延迟性
  3. RDD 是支持跨集群的分布式数据结构
  4. RDD 可以根据记录的 Key 对结构分区
  5. RDD 提供了粗粒度的操作,并且都支持分区

Spark 算子

Spark 应用程序的本质就是把要处理的数据转换为 RDD,然后将 RDD 通过一系列变换 Transformation 和操作 Action 得到结果,而这些变换和操作就是算子。

具体可以参考:

主要注意的是 Transformation 不会立即执行即可。

持久化操作有两种,cache() 是缓存到内存中,而 persist() 可以指定存储级别(具体可以参考笔记 2019-05-20-Python+Spark2.0机器学习与大数据实战.md,这里不赘述)

第 3 章 Spark 机制原理

Spark Application 是用户提交的应用程序。Spark 运行模式分别为:Local, Standalone, YARN, Mesos 等。

Spark Application 基本概念

  1. SparkContext:Spark Application 的入口,负责调度各个运算资源,协调各个 Worker Node 上的 Executor
  2. Driver Program:运行 Application 的 main() 函数并创建 SparkContext
  3. RDD:核心数据结构
  4. Worker Node:集群中任何可以运行 Application 代码的节点,运行一个或多个 Executor 进程
  5. Executor:为 Application 运行在 Worker Node 上的一个进程,该进程负责运行 Task,并且负责将数据存在内存或硬盘上

Spark Application 组件概念

  1. Task:RDD 中的一个分区对应一个 Task,Task 是单个分区上最小的处理流程单元
  2. TaskSet:一组关联的,但相互之间没有 Shuffle 依赖关系的 Task 集合
  3. Stage:一个 TaskSet 对应的调度阶段。每个 Job 会根据 RDD 的宽依赖被切分成很多 Stage,每个 Stage 都包含一个 TaskSet
  4. Job:由 Action 算子触发生成的由一个或多个 Stage 组成的计算作业
  5. Application:用户编写的 Spark 应用程序,由一个或多个 Job 组成。提交到 Spark 之后,Spark 为 Application 分配资源,将程序转换并执行
  6. DAGScheduler:根据 Job 构建基于 Stage 的 DAG,并提交 Stage 给 TaskScheduler
  7. TaskScheduler:将 Taskset 提交给 Worker Node 集群运行并返回结果

Spark Application 执行机制

Spark Application 从提交后到在 Worker Node 执行,期间经历了一系列变换,具体如下:

Spark 使用 BlockManager 管理数据块,在内存或者磁盘进行存储,如果数据不在本节点,则还可以通过远端节点复制到本机进行计算。在计算时,Spark 会在具体执行计算的 Worker 节点的 Executor 中创建线程池,Executor 将需要执行的任务通过线程池来并发执行。

应用提交与执行

Spark 使用 Driver 进程负责应用的解析、切分 Stage 并调度 Task 到 Executor 执行,包含 DAGScheduler 等重要对象。Driver 进程的运行地点有两种:

  1. 运行在 Client 端,对应用进行管理监控
  2. Master 节点指定某个 Worker 节点启动 Driver 进程,负责监控整个应用的执行

另外如果不用 Driver 还有另外两种模式:

  1. Mesos 模式:通过 mesos 来配置资源
  2. YARN 模式:提交的时候指定 --num-executors 等参数来进行资源申请

Job 的调度

在底层实现中,Action 算子最后调用了 runJob 函数提交 Job 给 Spark。其他的操作只是生成对应的 RDD 关系链

查看 SchedulingAlgorithm.scala 文件可以查看具体的调度模式,主要有如下几种:

  1. FIFO:默认情况,先进先出
  2. FAIR:采用轮询的方式为多个 Job 分配资源,所有任务优先级大致相同

当一个 Job 被提交后,DAGScheduler 会从 RDD 依赖链的末端触发,遍历整个 RDD 依赖链,划分 Stage。划分依据主要基于 ShuffleDependency 依赖关系(当某 RDD 在计算中需要将数据进行 Shuffle 操作时,包含 Shuffle 操作的 RDD 将会被用来作为输入信息,构成一个新的 Stage)。

  • 执行 Action 算子的 RDD 所在的 Stage 称为 Final Stage。DAGScheduler 会从这里生成作业实例

Spark 存储与 I/O

Spark 通信机制

分布式通信的基本方式:

  1. RPC(Remote Procedure Call Protocol)
  2. RMI(Remote Method Invocation)
  3. JMS(Java Message Service)
  4. EJB(Enterprise JavaBean)
  5. Web Service

Spark 通过 AKKA 来支持模块间的通信

容错机制及依赖

对于分布式系统,数据集的容错性通常有两种方式:

  1. 数据检查点(对应 Spark Checkpoint 机制)
  2. 记录数据的更新(对应 Spark Lineage 血统机制)
    1. 主要通过宽窄依赖进行处理

对于大数据分析而言,数据检查点操作成本较高,需要通过数据中心的网络连接在机器之间复制庞大的数据集,会消耗大量的存储资源。

Spark 选择记录更新的方式。但更新粒度过细时,记录更新成本也不低。因此,RDD 只支持粗粒度转换,即只记录单个块上执行的单个操作,然后将创建 RDD 的一系列变换序列记录下来,以便恢复丢失分区。

Shuffle 机制

Shuffle 是连接 Map 和 Reduce 之间的桥梁,Map 的输出要用到 Reduce 中必须经过 Shuffle 这个环节。

当 Map 的输出结果要被 Reduce 使用时,输出结果需要按关键字 key 哈希,并且分发到每一个 Reducer 上,这个过程就是 Shuffle

第 4 章 深入 Spark 内核

Spark Core 重点模块:

  1. Api:Java, Python 以及 R 的 API 实现
  2. Broadcast:广播变量的实现
  3. Deploy:部署与启动运行的实现
  4. Executor:Worker 节点负责计算部分的实现
  5. Metrics:运行时状态监控的实现
  6. Network:集群通讯实现
  7. Partial:近似评估代码
  8. Serializer:序列化模块
  9. Storage:存储模块
  10. UI:监控界面的代码逻辑实现

具体不深入,因为我主要是使用。同理略过下面几章:

  • 第 5 章 Spark on YARN
  • 第 6 章 BDAS 生态主要模块

第 7 章 Spark 调优

参数配置

对 Spark 性能的优化,最简单直接的方式就是调整参数。方式有:

  1. 添加配置到 spark-env.sh
  2. 动态载入属性,在代码中指定 spark-submit --name "wdxtub" --conf spark.executor.memory=8g ... myApp.jar
  3. 在代码中的 SparkConf 对象中设定参数

这三种方式的优先级从低到高

序列化优化

序列化对于任何分布式程序的性能有很大的影响。在很多情况下,开发者优化 Spark 应用的第一选择就是优化序列化。Spark 提供两个类库:

  1. Java 序列化:灵活,速度较慢,某些情况下序列化的结果也比较大
  2. Kryo 序列化:速度极快,紧凑,但并非支持所有类型,并且需要提前注册所使用的类

内存优化

主要关注如下三个方面:

  1. 对象占用的内存
  2. 访问对象的消耗
  3. 垃圾回收占用的内存开销

数据本地化

如果数据和操作数据的代码在同一个位置,那么运算会更快

其他并行考虑

  1. 并行度
  2. Reduce Task 的内存使用
  3. 广播“大变量”