Spark整理

2023-11-07

文章目录



1. 概述

在这里插入图片描述

Spark 是一种基于内存的快速、通用、可扩展的大数据分析计算引擎。最开始,Spark创建的目的就是解决Hadoop计算时间过长问题。

1.1. Spark 和 Hadoop 组成

在这里插入图片描述

  1. Hadoop
    • Hadoop 是由 java 语言编写的,在分布式服务器集群上存储海量数据并运行分布式分析应用的开源框架
    • HDFS理论基础源于Google的 TheGoogleFileSystem 论文,它是 GFS 的开源实现。
    • MapReduce 是 Google 的 MapReduce 论文实现。
    • 综合了 HDFS 的分布式存储和 MapReduce 的分布式计算,Hadoop 在处理海量数据时,性能横向扩展变得非常容易。
    • HBase 是对 Google 的 Bigtable 的开源实现,但又和 Bigtable 存在许多不同之处。HBase 是一个基于 HDFS 的分布式数据库,擅长实时地随机读/写超大规模数据集。它也是 Hadoop 非常重要的组件。
  2. Spark
    • Spark 是一种由 Scala 语言开发的快速、通用、可扩展的大数据分析引擎。
    • Spark Core 中提供了 Spark 最基础与最核心的功能。
    • Spark SQL 是 Spark 用来操作结构化数据的组件。通过 Spark SQL,用户可以使用SQL 或者 Apache Hive 版本的 SQL 方言(HQL)来查询数据。
    • Spark Streaming 是 Spark 平台上针对实时数据进行流式计算的组件,提供了丰富的处理数据流的 API。
    • Spark MLlib是 Spark 提供的一个机器学习算法库。MLlib 不仅提供了模型评估、数据导入等额外的功能,还提供了一些更底层的机器学习原语。
    • Spark GraphX 是 Spark 面向图计算提供的框架与算法库。

1.2. Spark 和 Hadoop 区别

  1. Spark 支持 DAG,能够满足 并行复用 场景

    Hadoop MapReduce 由于其设计初衷并不是为了满足循环迭代式数据流处理,因此在多并行可复用场景中存在诸多计算效率等问题。所以 Spark 应运而生(支持DAG, 数据复用性强),Spark 就是在传统的 MapReduce 计算框架的基础上,利用其计算过程的优化,从而大大加快了数据分析、挖掘的运行和读写速度,并将计算单元缩小到更适合并行计算和重复使用的 RDD 计算模型。

  2. Spark 和Hadoop 的根本差异是多个作业之间的数据通信问题 : Spark 多个作业之间数据通信是基于内存,而 Hadoop 是基于磁盘

  3. Spark Task 的启动时间快。Spark 采用 fork 线程的方式,而 Hadoop 采用创建新进程(通过JVM重用进行优化)的方式。

  4. Spark 只有在 shuffle 的时候将数据写入磁盘,而 Hadoop 中多个 MR 作业之间的数据交互都要依赖于磁盘交互

  5. Spark 支持 checkpoint,保存数据切断血缘,重用性大大增强。

经过上面的比较,我们可以看出在绝大多数的数据计算场景中,Spark 确实会比 MapReduce更有优势。但是 Spark 是基于内存的,所以在实际的生产环境中,由于内存的限制,可能会由于内存资源不够导致 Job 执行失败,此时,MapReduce 其实是一个更好的选择,所以 Spark并不能完全替代 MR。

2. Spark 运行架构

2.1. 基础架构

在这里插入图片描述

Spark 框架的核心是一个计算引擎,整体来说,它采用了标准 master-slave 的结构。Driver 表示 master,负责管理整个集群中的作业任务调度。图形中的 Executor 则是 slave,负责实际执行任务。

  1. Driver

    Spark 驱动器节点,用于执行 Spark 任务中的 main(),负责实际代码的执行工作。Driver 在 Spark 作业执行时主要负责:

    • 将用户程序转化为作业(job
    • 在 Executor 之间调度任务(task)
    • 跟踪 Executor 的执行情况
    • 通过 UI 展示查询运行情况
  2. Executor

    Spark Executor 是集群中工作节点(Worker)中的一个 JVM 进程,负责在 Spark 作业中运行具体任务(Task),任务彼此之间相互独。Spark 应用启动时,Executor 节点被同时启动,并且始终伴随着整个 Spark 应用的生命周期而存在。如果有 Executor 节点发生了故障或崩溃,Spark 应用也可以继续执行,会将出错节点上的任务调度到其他 Executor 节点上继续运行(同MapReduce)。核心功能:

    • 负责运行组成 Spark 应用的任务,并将结果返回给驱动器进程。
    • 它们通过自身的块管理器(Block Manager)为用户程序中要求缓存的 RDD 提供内存式存储RDD 是直接缓存在 Executor 进程内的,因此任务可以在运行时充分利用缓存数据加速运算。

2.2. Master & Worker(Standalone模式)

Spark 集群的独立部署环境中,不需要依赖其他的资源调度框架,自身就实现了资源调度的功能,所以环境中还有其他两个核心组件:Master 和 Worker,这里的 Master 是一个进程,主要负责资源的调度和分配,并进行集群的监控等职责(相当于 Yarn 的 ResourceManager)。而Worker 呢,也是进程,一个 Worker 运行在集群中的一台服务器上,由 Master 分配资源对数据进行并行的处理和计算(类似于 Yarn 环境中 NodeManager)。

缺点:只支持简单的固定资源分配策略,每个任务固定数量的 core,各 Job 按顺序依次分配在资源,资源不够的时候就排队,一般使用 Yarn 实现资源调度。

2.3. ApplicationMaster

Hadoop 用户向 YARN 集群提交应用程序时,提交程序中应该包含 ApplicationMaster(相当于 Yarn的MRAppMaster),用于向资源调度器申请执行任务的资源容器 Container,运行用户自己的程序任务 job,监控整个任务的执行,跟踪整个任务的状态,处理任务失败等异常情况。说的简单点就是,ResourceManager(资源)和 Driver(计算)之间的解耦合靠的就是ApplicationMaster。

3. Spark 编程

3.1. 数据结构

  • RDD : 弹性分布式数据集

    是 Spark 中最基本的数据处理模型。是一个抽象类,它代表一个弹性的、不可变(产生新的)、不保存数据、可分区、里面的元素可并行计算的集合。主要用于将逻辑进行封装,并生成 Task 发送给Executor 节点执行计算

  • ACC 累加器:分布式共享只写变量

    累加器用来把 Executor 端变量信息聚合到 Driver 端。在 Driver 程序中定义的变量,在Executor 端的每个 Task 都会得到这个变量的一份新的副本,每个 task 更新这些副本的值后,传回 Driver 端进行 merge,从而实现共享可写功能。

  • BC 广播变量:分布式共享只读变量

    广播变量用来Driver向Executor高效分发一个较大的只读值,以供一个或多个 Spark 操作使用。比如,如果你的应用需要向所有节点发送一个较大的只读查询表。在多个并行操作中使用同一个变量,Spark 会为每个任务分别发送。实现共享可读功能。

    广播变量在Driver定义与赋值,Executor端只读。Driver无法将一个RDD广播出去,因为RDD无法存储数据,但是可以将RDD结构广播出去,如 rdd.collect( )。广播变量一般存储在 Execution 区以实现共享。

3.2. RDD算子类型

Transformation算子:即从现有的数据集RDD创建一个新的数据集RDD,所以叫做 Transformation 转化算子。

Action算子:即在数据集上进行计算后,返回一个值给 Driver 程序。

RDD 中所有的 Transformation 都是惰性的,也就是说,它们并不会直接计算结果。相反的它们只是记住了这些 应用到基础数据集(例如一个文件)上的转换动作。只有当发生一个要求返回结果给 Driver 的 Action 时,这些 Transformation 才会真正运行,这个设计让 Spark 更加有效的运行。

3.2.1. RDD转换算子

RDD 根据数据处理方式的不同将算子整体上分为 Value 类型、双 Value 类型和 Key-Value 类型

  1. Value 类型

    • map:将处理的数据逐条进行映射转换,这里的转换可以是类型的转换,也可以是值的转换。
    • mapPartitions:将处理的数据逐条进行映射转换,这里的转换可以是类型的转换,也可以是值的转换。
    • mapPartitionsWithIndex:将待处理的数据以分区为单位发送到计算节点进行处理,这里的处理是指可以进行任意的处理,哪怕是过滤数据,在处理时同时可以获取当前分区索引。
    • flatMap:将处理的数据进行扁平化后再进行映射处理,所以算子也称之为扁平映射。
    • glom:将同一个分区的数据直接转换为相同类型的内存数组进行处理,分区不变。
    • groupBy:将数据根据指定的规则进行分组, 分区默认不变,但是数据会被打乱重新组合,我们将这样的操作称之为 shuffle。极限情况下,数据可能被分在同一个分区中。
    • filter:将数据根据指定的规则进行筛选过滤,符合规则的数据保留,不符合规则的数据丢弃。当数据进行筛选过滤后,分区不变,但是分区内的数据可能不均衡,生产环境下,可能会出现数据倾斜
    • sample:根据指定的规则从数据集中抽取数据。
    • distinc:将数据集中重复的数据去重。
    • coalesce:根据数据量缩减分区,用于大数据集过滤后,提高小数据集的执行效率当 spark 程序中,存在过多的小任务的时候,可以通过 coalesce 方法,收缩合并分区,减少分区的个数,减小任务调度成本。可能发生shuffle,参数控制。
    • repartition:该操作内部其实执行的是 coalesce 操作,参数 shuffle 的默认值为 true。无论是将分区数多的RDD 转换为分区数少的 RDD,还是将分区数少的 RDD 转换为分区数多的 RDD,repartition操作都可以完成,因为无论如何都会经 shuffle 过程。
    • sortBy:该操作用于排序数据。在排序之前,可以将数据通过 f 函数进行处理,之后按照 f 函数处理的结果进行排序,默认为升序排列。排序后新产生的 RDD 的分区数与原 RDD 的分区数一致。中间存在 shuffle 的过程。
  2. 双 Value 类型

    • intersection:对源 RDD 和参数 RDD 求交集后返回一个新的 RDD。
    • union:对源 RDD 和参数 RDD 求并集后返回一个新的 RDD。
    • subtract:以一个 RDD 元素为主,去除两个 RDD 中重复元素,将其他元素保留下来。求差集。
    • zip:将两个 RDD 中的元素,以键值对的形式进行合并。其中,键值对中的 Key 为第 1 个 RDD中的元素,Value 为第 2 个 RDD 中的相同位置的元素。
  3. Key-Value 类型

    • partitionBy:将数据按照指定 Partitioner 进行重分区。Spark 默认的分区器是 HashPartitioner。

    • reduceByKey:可以将数据按照相同的 Key 对 Value 进行聚合,在shuffle之前有combine(预聚合)操作,但不是任何场景都适用。

    • groupByKey:将数据源的数据根据 key 对 value 进行分组。

    • aggregateByKey:将数据根据不同的规则进行分区内计算和分区间计算。

    • foldByKey:当分区内计算规则和分区间计算规则相同时,aggregateByKey 就可以简化为 foldByKey。

    • combineByKey:最通用的对 key-value 型 rdd 进行聚集操作的聚集函数(aggregation function)。类似于aggregate(),combineByKey()允许用户返回值的类型与输入不一致。

    • sortByKey:在一个(K,V)的 RDD 上调用,K 必须实现 Ordered 接口(特质),返回一个按照 key 进行排序。

    • join:在类型为(K,V)和(K,W)的 RDD 上调用,返回一个相同 key 对应的所有元素连接在一起的 (K,(V,W)) 的 RDD。

    • leftOutJoin:类似于 SQL 语句的左外连接。

    • cogroup:在类型为(K,V)和(K,W)的 RDD 上调用,返回一个(K,(Iterable,Iterable))类型的 RDD。

3.2.2. RDD行动算子
  • reduce

    聚集 RDD 中的所有元素,先聚合分区内数据,再聚合分区间数据

  • collect

    在驱动程序中,以数组 Array 的形式返回数据集的所有元素

  • count

    返回 RDD 中元素的个数

  • first

    返回 RDD 中的第一个元素

  • take

    返回一个由 RDD 的前 n 个元素组成的数组

  • takeOrdered

    返回该 RDD 排序后的前 n 个元素组成的数组

  • aggregate

    分区的数据通过初始值和分区内的数据进行聚合,然后再和初始值进行分区间的数据聚合

  • fold

    折叠操作,aggregate 的简化版操作

  • countByKey

    统计每种 key 的个数

  • save

    将数据保存到不同格式的文件中

  • foreach

    分布式遍历 RDD 中的每一个元素,调用指定函数

3.2.3. RDD控制算子

crontroller 即控制算子,不直接参与计算,但是对性能和效率的有很好的支持

比如 cache 和 persist,详见 [Cache/Persist 缓存](#3.4.1. RDD Cache/Persist 缓存)

3.3. RDD作业的划分

RDD 任务切分中间分为:Application、Job、Stage 和 Task

  • Application:初始化一个 SparkContext 即生成一个 Application;

  • Job:一个 Action 算子就会生成一个 Job( runJob ( ) );

  • Stage:Stage 等于宽依赖(ShuffleDependency)的个数加 1(第一个创建的ResultStage阶段);

  • Task:一个 Stage 阶段中,最后一个 RDD 的分区个数就是 Task 的个数,而Task数量其实就是并行度。

注意:Application->Job->Stage->Task 每一层都是 1 对 n 的关系。

3.4. Job的调度模式

用户通过不同的线程提交的Job可以并发运行,但是受到资源的限制。Job到调度池(pool)内申请资源,调 度池会根据工程的配置,决定采用哪种调度模式。

  1. FIFO模式 (先进先出)

    默认情况下,Spark调度器以FIFO(先进先出)方式调度Job的执行。每个Job被切分为多个Stage。无论是Job还是Stage,都是前面的优先分配。如果前面的资源分配完成,还剩下资源的话,后面的Job可以获取剩下的,可能存在多个Job并行运行。

  2. FAIR模式(公平)

    从spark0.8开始,可以配置公平调度器,在FAIR共享模式调度下,Spark在多Job之间以轮询(round robin)方式为任务分配资源,所有的任务拥有大致相当的优先级来共享集群的资源。这就意味着当一个长任务正在执行时,短任务仍可以分配到资源,提交并执行, 并且获得不错的响应时间。这样就不用像以前一样需要等待长任务执行完才可以。这种调度模式很适合多用户的场景。

3.5. RDD阶段划分

在这里插入图片描述

RDD task任务 的执行是分 stage阶段的,后一个阶段必须要等前一个阶段执行完成才能执行

  1. 提交任务,创建 DAGSchdule,开始划分阶段 createResultStage
  2. 无论有没有后续阶段,最开始都创建一个 ResultStage
  3. 父阶段是否存在,不存在线创建父阶段 getOrCreateParentStages
  4. 判断是否为 Shuffle依赖 ,如果是则添加一个 ShuffleDependency
  5. 创建完成后,获取所有的 Shuffle依赖 getShuffleDependencies,获取每一个Shuffle依赖的阶段,如果没有就新建一个阶段getOrCreateShuffleMapStage
  6. 存在Shffle依赖的父RDD分配到上一个阶段,而子RDD别分配到下一个阶段。

3.6. RDD依赖关系

3.6.1. RDD血缘关系

RDD 只支持粗粒度转换,即在大量记录上执行的单个操作。将创建 RDD 的一系列 Lineage(血缘)记录下来,以便恢复丢失的分区。RDD 的 血缘 会记录 RDD 的元数据信息和转换行为,当该 RDD 的部分分区数据丢失时,它可以根据这些信息来以及checkpoint来重新运算和恢复丢失的数据分区。相邻的 RDD 存在 依赖关系,即子RDD依赖父RDD,多个连续的 RDD 依赖关系称之为 血缘关系。

3.6.2. 窄依赖

窄依赖表示每一个父(上游)RDD 的 Partition 最多被子(下游)RDD 的一个 Partition 使用,窄依赖相当于独生子女,窄依赖联系的父RDD和子RDD可以共享一个Task。窄依赖可以支持在同一个集群节点上,执行多条命令(也叫同一个 stage 的操作), 例如在执行了 map 后,紧接着执行 filter。而且窄依赖计算失败后只需要重算失败的那个分区数据即可。

3.6.3. 宽依赖

宽依赖表示同一个父(上游)RDD 的 Partition 被多个子(下游)RDD 的 Partition 依赖,会引起 Shuffle 详见 [Shuffle机制](#3.7. Shuffle 机制),宽依赖相当于 二胎三胎。由于宽依赖会产生Shuffle,需要两个Task分阶段分别完成父RDD和子RDD。宽依赖计算失败,需要将关系的所有分区数据都重算才可以。

3.7. Shuffle 机制

当父RDD与子RDD形成宽依赖关系时,中间需要进行Shuffle。Shuffle的主要目的就是将父RDD的分区数据根据配置的分区规则(默认hashPartitoner),划分为子RDD需要的数量和规则要求的分区数据。

3.7.1. Shuffle策略

Spark的每个 Stage 阶段都会有一次 Shuffle,根据 Stage 的 Shuffle 特点,将 Task 分为 ShuffleMapTask 和 ResultTask。其中 ResultTask 就是Stage最后一个也是经过 Shuffle 之后的 Task,其它都是 ShuffleMapTask。

由 ShuffleMapTask 到 ResultTask,主要有以下两个 Shuffle 策略:

3.7.1.1. HashShuffle
  1. 未经优化的HashShuffle

在这里插入图片描述

  • Shuffle write

    每一个ShufflleMapTask会为每一个ResultTask创建一个bucket缓存,并且会为每一个bucket创建一个文件block file。这个bucket存放的数据就是经过Partitioner操作(默认是HashPartitioner)之后找到对应的bucket然后放进去,最后将数据bucket缓存数据写入磁盘block flie。

  • Shuffle read

    ResultTask 会讲需要的所有block file 读取到内存当中,如果内存不足,同时时候磁盘。

缺点是每个 ShuffleMapTask 都会产生 reduce 数量个 buket 缓存和 block 文件。如果 ShuffleMapTask 数量很多而且数据量很大,首先内存方面很容易不足,然后就是需要持久化的文件个数过多,效率不高。

  1. 优化后的 HashShuffle
    在这里插入图片描述

    • 优化内容:

      有原来的每个Task输出block flie改编为每个Executor(Executor内部可能对应多个task)输出block file。比优化前的 hashshuffle 的文件数量少了很多。

    优点:
    同一个 Executor 的所有并行 ShuffleMapTask 可以将数据输出到同一个blockflie,大大减少 block file 数量,提高性能。

    缺点:

    1. 多个 Executor 情况下,block file 并不是最优最少即 ResultTask数量个,而是ResultTask * Executor 数量个,有优化空间。
    2. hashShuffle 并不提供数据排序功能。
3.7.1.2. SortShuffle
  1. 普通的 SortShuffle
    在这里插入图片描述

    Spark 借鉴了 MapReduce 的 Shuffle 处理,使得 ShuffleMapTask 输出的数据文件数量等于 ResultTask 数量个,并且有序(相当于将Reduce阶段的计算放在Map阶段,实现优化)。步骤如下:

    1. 缓存:ShuffleTask先将数据写入内存数据结构,一般使用Map数据结构,再写入缓存。
    2. 排序:如果内存数据结果达到了一定阈值,对数据结构的数据进行排序。
    3. 溢写:对排序的数据,分批溢写临时文件到磁盘。
    4. 归并排序合并:合并分区数据,并进行归并排序。
  2. bypass的SortShuffle

在这里插入图片描述
触发条件:

  1. 非聚合算子,比如reduceByKey
  2. shuffle read task数量小于某个阈值(spark.shuffle.sort.bypassMergeThreshold,默认为 200)

优势:

  1. Shuffle write 阶段可以直接写入缓存。
  2. 不进行排序,节省排序性能开销。
3.7.2. Shuffle 分区器
  • HashPartitioner:

    Spark 在做 Shuffle 时,默认使用 HashPartitioner对数据进行分区。如果并行度设置的不合适,可能造成大量不相同的 Key 应的数据 被分配到了同一个 Task 上,造成该 Task 所处理的数据远大于其它 Task,从而可能造成数据倾斜。如果调整 Shuffle 时的并行度,使得原本被分配到同一 Task 的不同 Key 发配到不同 Task 上处理,则可降低原 Task 所需处理的数据量,从而缓解数据倾斜问题造成的短板效应。

  • RangerPartitioner:

    首先使用 水塘抽样 获取样品数据,再通过内部计算得到边界数组,将数据按照边界数据进行范围划分,以范围为单位划分给每个分区,分界的算法尤为重要,对应的函数是rangeBounds。 。数据比较均衡,并且分区内数据是个范围,实现分区间有序。

  • 自定义分区器:

3.8. RDD 持久化

3.8.1. RDD Cache/Persist 缓存

在这里插入图片描述

RDD 通过 Cache(本质是persist(StorageLevel.MEMORY_ONLY)) 或者 Persist 方法将前面的计算结果(触发action 算子时)缓存,该 RDD 将会被缓存在计算节点的内存中,并供后面多路重用或失败重算

Spark 会自动对一些 Shuffle 操作的中间数据做持久化操作(比如:reduceByKey)。这样做的目的是为了当一个节点 Shuffle 失败了避免重新计算整个输入。但是,在实际使用的时候,如果想重用数据,仍然建议调用 persist 或 cache。

3.8.2. RDD CheckPoint 检查点

所谓的检查点其实就是通过将 RDD 中间结果写入磁盘。

由于血缘依赖过长会造成容错成本过高,这样就不如在中间阶段做检查点容错,如果检查点之后有节点出现问题,可以从检查点开始重算血缘,减少了开销。对 RDD 进行 checkpoint 操作并不会马上被执行,必须执行 Action 操作才能触发

3.8.3. 缓存和检查点区别
  1. Cache 缓存只是将数据保存起来,不切断血缘依赖。CK 检查点切断血缘依赖
  2. Cache 缓存的数据通常存储在磁盘、内存等地方,可靠性低。CK 的数据通常存储在 HDFS 等容错、高可用的文件系统,可靠性高。
  3. CK过程会额外提交一次任务。
  4. 建议对CK的RDD 使用 Cache 缓存,这样 CK 的 job 只需从 Cache 缓存中读取数据即可,否则需要再从头计算一次 RDD。

4. Spark 工作流程 *

4.1. Spark 工作流程

在这里插入图片描述

  1. Spark部署方式,详见[Spark部署方式](#4.2. Spark 部署方式)。实际选用 Yarn ClusterX方式。

    脚本启动,SparkSubmit创建一个 Yarn Client,通过反射执行main( )。

spark-submit \ 
--master local[5] \ 
--driver-cores 2 \ # driver使用内核数,默认为1
--driver-memory 8g \ # driver内存大小,默认512M
--executor-cores 4 \ # 每个executor内核数,默认为1,官方建议2-5个,我们企业是4个
--num-executors 10 \ # 启动executors的数量,默认为2
--executor-memory 8g \ # executor内存大小,默认1G
--class PackageName.ClassName XXXX.jar \ 
--name "Spark Job Name" \ 
InputPath \ 
OutputPath
  1. 封装并发送给 ResourceManager,Yarn Cluster 模式 的Client已完成它的工作,后续断连无碍任务执行。
  2. RM 挑选健康的 NodeManager,启动ApplicationMaster,AM 向 RM 申请资源,并监督作业的运行状况。
  3. AM 启动 Driver,Drvier 负责创建 SparkContext,划分stage/taskset,并执行Driver代码。
  4. Driver通过ClusterManager请求资源创建ExecutorBackend,在ExecutorBackend中创建Executor,创建完成后,Executor需要向Driver注册自己。
  5. Driver的SparkContext中,创建DAGSchedule 和 TaskSchedule 两个调度器。每执行一个Action就创建一个 Job,Job会提交DAGSchedule。
    • DAGSchedule
      1. DAG 详见 [DAG有向无环图](#4.3. DAG 有向无环图)。
      2. DAGScheduler负责Spark的最高级别的任务调度,调度的粒度是Stage。
        DAGSchduler首先要对作业进行划分,详见 [RDD作业划分](#3.3. RDD作业的划分)。
      3. DAGSchduler调度粒度是Stage,关于Stage的划分,详见 [stage阶段划分](#3.5. RDD阶段划分)。
      4. DAGSchduler为每个Job的所有Stage计算一个DAG,为每个DAG计算出taskset,taskset非常重要,因为她决定了Stage的并行度,详见[Stage并行度](#5. Spark 并行度)。
      5. 最后将每个Stage的taskset交给TaskSchedule。
    • TaskSchedule
      1. TaskSchdule计算出taskset,taskset非常重要,因为她决定了Stage的并行度,详见[Stage并行度](#5. Spark 并行度)。
      2. TaskScheduler从DAGScheduler的每个Stage接收taskset,并负责将它们发送到集群上运行它们,如果出错还会重试。Spark的 Job 调度模式,详见 [调度模式](#3.3.6. Job的调度模式)。
      3. TaskScheduler从DAGScheduler的每个Stage接收taskset,并负责将它们发送到集群上运行它们,如果出错还会重试。
      4. 最后返回消息给DAGScheduler。TaskSchdule发送taskset需要用到调度器,Spark除了自身提供的调度器外,还支持 Yarn 的调度器。
  6. taskset被发送到Executor中,Executor创建Task容器执行taskset,计算完成后,将结果返回给Driver。

4.2. Spark 部署方式

  1. Local:运行在一台机器上,通常是练手或者测试环境。
  2. Standalone:构建一个基于Master+Slaves的资源调度集群,Spark任务提交给Master运行。是Spark自身的一个调度系统。 缺点:只支持简单的固定资源分配策略,每个任务固定数量的 core,各 Job 按顺序依次分配在资源,资源不够的时候就排队。
  3. Yarn Client:Client 模式将用于监控和调度的 Driver 模块在Yarn 客户端执行,而不是在 Yarn 中,所以一般用于测试。优点是方便调试及查看各种日志,缺点是Client连接中断,任务就会失败。
  4. Yarn Cluster:Cluster 模式将用于监控和调度的 Driver 模块启动在 Yarn 集群资源中执行。一般应用于实际生产环境。优点是Client连接中断,任务继续执行,日志需要登录某个节点查看。

Yarn 模式的优点:

  1. Spark 支持资源动态共享,运行于 Yarn 的框架都共享一个集中配置好的资源池
  2. 可以 很方便的利用 Yarn 的资源调度特性来做分类,隔离以及优先级控制负载,拥有更灵活的调度策略
  3. Yarn 可以 自由地选择 executor 数量
  4. Yarn 支持 Spark 安全的集群管理器,使用 Yarn,Spark 可以运行于 Kerberos Hadoop 之上,在它们进程之间进行安全认证

4.3. DAG 有向无环图

在这里插入图片描述

DAG(Directed Acyclic Graph)有向无环图是由点和线组成的拓扑图形,该图形具有方向,不会闭环

Spark 将计算程序根据依赖关系映射成点与线组成的 DAG 有向无环图,并且划分层级,详见 [层级划分](#3.3.5. RDD作业的划分)。

Job 和 Stage 存在执行顺序,串行处理,而 多个Task 之前并没有依赖关系,可以并行处理。关于 Stage 的划分,其实是根据RDD依赖关系划分的,详细见 [RDD依赖关系](#3.3. RDD依赖关系)。

5. Spark 并行机制

Spark将一个Application划分为多个Job作业,每个Job作业又划分多个Stage,而Executor的Task线程实际执行的是Stage的 taskset。Spark的并行指的某个Stage阶段,同时有多个Task线程在执行task任务。

Spark的并行的Task,一个Task处理一个分区的数据。如果要确定下游的并行结构,就需要确定两个因素。就是 并行度(即分区数) 和 分区规则。简单来说就是先确定有多少的并行Task线程,再将各个并行Task线程的输入数据按照分区规则分配给执行的Task线程。

5.1. 并行度

每个Stage的并行度,代表了这个Stage的同时计算能力,并行度越大,处理相同数据量时间越小,并行度的影响因素主要有 资源并行度数据并行度。有足够的资源才能启动足够数量的并行Task线程,有了足够的并行Task线程,就可以为每一个分区数据进行并行计算。

  1. 资源的并行度:

    由节点数( executor )和 cpu 数( core )决定的。

    –num-executors(Executor 的数量);

    –executor-memory(每个 Executor 的内存大小);

    –executor-cores( Executor 的 core 数量)。

  2. 数据的并行度: 并行 task 的数据,parttion 大小

  3. 影响分区的因素:

    • 启动时置顶默认并行度 spark.default.parallelism , 或 SparkContext 的 setMaster("local[*]")

    • 对于Scala集合分发到RDD,可以使用 sc.parallelize(scala集合, 并行度)。优先级高于spark.default.parallelism,但是不要大于这个值。

    val rdd: RDD[Int] = sc.parallelize(List(1, 2, 3), 3)

    
    * 读取文件是设置并行度
    
    1. `textFile(path,minPartitions)`
    
       ```scala
       // partition最大值是2
     math.min(defaultParallelism, 2);
       ```
    
    2. `wholeTextFile(path, minPartitions)`
    
       ```scala
       // partition最小值是2
       max(minPartitions, 2);
       ```
    
    3. Shuffle 改变分区数量。比如 rePartition,groupBykey...
    4. 自定义分区改变分区数量。
    
    

如何合理设置 资源 及 并行度(task数量):

Task数量 与 资源关系

  • task数量少,资源剩余,直接浪费了差值资源。
  • task数量和资源数量相同,先完成的task资源无法及时释放。
  • task数量多,资源不足,执行完一批task,在执行下一批task,资源重用。

所以,task数量要相应多于资源数量,官方推荐 Task数量为 core 总数 2~3 倍最佳

合理配置初始并行度和资源时候,先再根据数据量,确定初始Task数量,反推资源数量(2~3倍),完成合理配置。

5.2. 分区规则

并行度的确定,只是确定了下游有几个Task线程,但是无法知道每一个Task线程需要计算的数据是什么。Spark是 一个Task线程处理一个分区的数据,而上游分区分配到下游分区,需要分区器来定义分区规则。

6. Spark数据倾斜

6.1. 什么是数据倾斜

对 Spark/Hadoop 这样的大数据系统来讲,数据量大并不可怕,可怕的是数据倾斜。数据倾斜指的是,并行处理的数据集中,某一部分(如 Spark 或 Kafka 的一个 Partition)的数据显著多于其它部分,从而使得该部分的处理速度成为整个数据集处理的瓶颈(木桶效应)。

6.2. 数据倾斜产生原因

在 Spark 中,同一个 Stage 的不同 Partition 可以并行处理,而具有依赖关系的不同 Stage 之间是串行处理的。数据倾斜往往发生在同一个Stage不同的task中,即个别task计算时间远远长于其他task。而相同 Stage 内的所有 Task 都执行相同的计算,在排除不同计算节点计算能力差异的前提下,不同 Task 之间耗时的差异主要由该 Task 所处理的数据量决定。

6.3. 解决方案

Spark 数据倾斜的几种场景及对应的解决方案 调整并行度自定义Partitioner使用 Map侧Join 代替 Reduce侧Join(内存表合并),给倾斜 Key 加上随机前缀等。

  1. 调整并行度分散同一个 Task 的不同 Key

    在这里插入图片描述

    • HashPartitioner:

      Spark 在做 Shuffle 时,默认使用 HashPartitioner对数据进行分区。如果并行度设置的不合适,可能造成大量不相同的 Key 应的数据 被分配到了同一个 Task 上,造成该 Task 所处理的数据远大于其它 Task,从而造成数据倾斜。如果调整 Shuffle 时的并行度,使得原本被分配到同一 Task 的不同 Key 发配到不同 Task 上处理,则可降低原 Task 所需处理的数据量,从而缓解数据倾斜问题造成的短板效应。

    • RangerPartitioner:

      首先使用 水塘抽样 获取样品数据,再通过内部计算得到边界数组,将数据按照边界数据进行范围划分,以范围为单位划分给每个分区。数据比较均衡,并且分区内数据是个范围。

  2. 自定义Partition
    使用自定义的 Partitioner(默认为 HashPartitioner),将原本被分配到同一个 Task 的不同Key 分配到不同 Task,可以拿上图继续想象一下,通过自定义 Partitioner 可以把原本分到 Task0 的 Key分到Task1,那么 Task0 的要处理的数据量就少了。

  3. 将 Reduce side Join 转变为 Map side Join
    在这里插入图片描述
    通过 Spark 的 Broadcast 机制,将 Reduce side Join 转化为 Map side Join,避免 Shuffle 从而完全消除 Shuffle 带来的数据倾斜,但是有内存溢出的风险。

  4. 为 skew 的 key 增加随机前/后缀
    在这里插入图片描述
    为数据量特别大的 Key 增加随机前/后缀,使得原来 Key 相同的数据变为Key 不相同的数据,从而使倾斜的数据集分散到不同的 Task 中,彻底解决数据倾斜问题。Join 另一则的数据中, 倾斜 Key 对应的部分数据,与随机前缀集作笛卡尔乘积,从而保证无论数据倾斜侧的 Key 如何加前缀,都能正常 Join。

  5. 大表随机添加 N 种随机前缀,小表扩大 N 倍

    如果出现数据倾斜的 Key 比较多,上一种方法将这些大量的倾斜Key 分拆出来,意义不大(很难一个 Key 一个 Key 都加上后缀)。此时更适合直接对存在数据倾斜的数据集全部 加上随机前缀,然后对另外一个不存在严重数据倾斜的数据集整体与随机前缀集作笛卡尔乘积(即将数据量扩大 N 倍),可以看到RDD2 扩大了 N 倍了,再和加完前缀的大数据做笛卡尔积。

8. Spark 内存模型

8.1. 堆内内存 和 堆外内存

Spark的Executor的内存管理是基于JVM的内存管理之上,Spark对JVM堆内(On-Heap)空间进行了更为详细的分配, 以便充分利用内存,同时Spark引入堆外内存(OffHeap),可以直接在Worker节点的系统内存中开辟空间,进一步优化内存使用。

  • 堆内空间(On-Heap)

    Spark的堆内(On-Heap)空间是由--executor-memory参数配置,Executor内运行的并发任务共享JVM堆内内存。而且该堆内内存是一种逻辑上的管理,因为对象的释放都是由JVM完成。

  • 堆外内存(Off-Heap)

    主要是为了提高Shuffle排序的效率,存储优化过的二进制数据。从2.0之后Spark 可以直接操作系统的堆外内存,减少不必要的开销。默认不开启,spark.memory.offHeap.ennable 参数启用,并由spark.memory.offHeap.size 参数设定堆外空间大小。

Spark 1.6之前使用的是静态内存管理( StaticMemoryManager )机制 StaticMemoryManager 也是 Spark 1.6之前唯一的内存管理器。在Spark1.6之后引入了统一内存管理( UnifedMemoryManager )机制, 是 Spark 1.6之后默认的内存管理器。

8.2. 堆外内存模型

默认情况下,Spark 仅仅使用了堆内内存。Executor 端的堆内内存区域大致可以分为以下四大块:

  • Execution 内存:主要用于存放 Shuffle、Join、Sort、Aggregation 等计算过程中的临时数据。Excutor内运行的Task,共享 Execution 内存。

  • Storage 内存:主要用于存储 spark 的 cache 数据,例如RDD的缓存、unroll数据。

    Execution 和 Storage 内存中间存在内存动态调整机制,即任何一方内存不够而另一方内存空闲的情况下,可以申请到对方的一部分内存自己使用。当Execution 需要使用内存对方占用时候,可让对方将占用的部分转存到硬盘,然后"归还"借用的空间。反之 Storage 无法让对方 “归还”,这也是应该 Shuffle 存储情况复杂,可能会被后面任务使用。

  • User 内存:主要用于存储 RDD 转换操作所需要的数据,例如 RDD 依赖等信息。

  • 预留内存:系统预留内存,会用来存储Spark内部对象。
    在这里插入图片描述

9. Spark 知识点

9.1. Repartition和Coalesce关系与区别

  • 关系:

    两者都是用来改变RDD的partition数量的,repartition底层调用的就是coalesce方法:coalesce(numPartitions, shuffle = true)

  • 区别:

    repartition一定会发生shuffle,coalesce根据传入的参数来判断是否发生 shuffle。一般情况下增大rdd的partition数量使用repartition,减少partition数量时使用coalesce

9.2. 小文件的读取

读取的数据源为非常多的小文件时,如果使用 textFile() 读取数据,那么一个文件就是一个 Task,并且要为每个文件建立一个分区,效率低下。这时候可以使用 wholeTextFiles进行大量小文件数据的读取,wholeTextFiles 通过设置的文件路径将小文件都读取完成后,会通过设置的 defaultParallelism,创建相应的分区和Task,如果没有设置,默认为2个。

9.3. Spark 优化

  1. 平台层面的调优:防止不必要的jar包分发,提高数据的本地性,选择高效的存储格式如parquet。
  2. 应用程序层面的调优:过滤操作符的优化降低过多小任务, 降低单条记录的资源开销,处理数据倾斜,复用RDD进行缓存,作业并行化执行等等。
  3. JVM层面的调优:设置 合适的资源量,设置合理的JVM,启用高效的序列化方法如kyro,增大off head内存等等

9.4. CPU 密集型场景

  • 场景描述:

    I/O在很短的时间就可以完成,而 CPU 还有许多运算 要处理,CPU Loading 很高。一般是需要进行大量计算,比如逻辑判断,机器学习 或 图计算等,这是 CPU 成为性能瓶颈。

  • 优化点:

    1. 降低任务的并行执行,业务越多,任务切换的性能开销就越多,即CPU开销就越高。
    2. 优化计算逻辑,减少计算逻辑的复杂度。
    3. 尽量减少使用高强度压缩方式,对原始数据的压缩和解压缩会增加CPU的负担
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Spark整理 的相关文章

  • spark性能优化调优指导性文件

    1 让我们看一下前面的核心参数设置 num executors 10 20 executor cores 1 2 executor memory 10 20 driver memory 20 spark default parallelis
  • Spark集群安装部署

    目录 一 环境准备 二 安装步骤 三 使用Standalone模式 四 使用Yarn模式 一 环境准备 由于Spark仅仅是一种计算机框架 不负责数据的存储和管理 因此 通常都会将Spark和Hadoop进行统一部署 由Hadoop中的HD
  • spark创建maven工程创建scala目录并编译

    背景 我创建spark的maven工程的时候 在java目录同级还创建了一个scala目录 这就得考虑编译相关的事了 解决 1 创建source folder 如下图所示 直接创建就好了 2 编译带来的问题 编译的时候发现一个问题 就是在s
  • Spark性能调优之Shuffle调优

    Spark性能调优之Shuffle调优 Spark底层shuffle的传输方式是使用netty传输 netty在进行网络传输的过程会申请堆外内存 netty是零拷贝 所以使用了堆外内存 shuffle过程中常出现的问题 常见问题一 redu
  • Spark基础知识(个人总结)

    声明 1 本文为我的个人复习总结 并非那种从零基础开始普及知识 内容详细全面 言辞官方的文章 2 由于是个人总结 所以用最精简的话语来写文章 3 若有错误不当之处 请指出 一 Spark概述 Spark模块 Core SQL Streami
  • 【pyspark】DataFrame基础操作(二)

    介绍一下 pyspark 的 DataFrame 基础操作 一 选择和访问数据 PySpark DataFrame 是惰性计算的 简单地选择一列不会触发计算 但它会返回一个 Column 实例 并且 大多数按列操作都返回 Column 实例
  • 学习大数据spark——心得体会

    总结与体会 1 项目总结 本次项目实现了Spark 单机模式Python版的安装 介绍了与Spark编程有关的一些基本概念 特别对RDD的创建 转换和行动操作做了比较详细的说明 对从RDD 到DataFrame的实现进 行了案例训练 包括
  • 【Spark系列2】reduceByKey和groupByKey区别与用法

    在spark中 我们知道一切的操作都是基于RDD的 在使用中 RDD有一种非常特殊也是非常实用的format pair RDD 即RDD的每一行是 key value 的格式 这种格式很像Python的字典类型 便于针对key进行一些处理
  • 大数据相关常用软件下载地址集锦

    文章目录 每日一句正能量 前言 一 软件下载地址如下 二 文档地址如下 结语 每日一句正能量 生命中有一些人与我们擦肩了 却来不及遇见 遇见了 却来不及相识 相识了 却来不及熟悉 熟悉了 却还是要说再见 前言 由于大数据开发中经常需要用到Z
  • spark算子执行位置研究,driver端?executor端?

    参考资料 https cloud tencent com developer article 1545723 前言 spark算子的执行位置 driver端 还是executor端 这些之前其实没有注意过 最近在学流处理 发现这个还是很重要
  • Spark SQL 之 Temporary View

    Spark SQL 之 Temporary View spark SQL的 temporary view 是支持原生SQL 的方式之一 spark SQL的 DataFrame 和 DataSet 均可以通过注册 temporary vie
  • spark-submit 报错 Initial job has not accepted any resources

    spark submit 报这样的错误 WARN scheduler TaskSchedulerImpl Initial job has not accepted any resources check your cluster UI to
  • spark中repartition和coalesce的区别

    总的来讲 两者 对是否允许shuffle 不同 coalesce numPartitions shuffle false repartition numPartitions repartition 其实是 coalesce 中参数shuff
  • spark_hadoop集群搭建自动化脚本

    bin bash 脚本使用说明 1 使用脚本前需要弄好服务器的基础环境 2 在hadoop的每个节点需要手动创建如下目录 data hdfs tmp 3 修改下面的配置参数 4 脚本执行完备后需要收到格式化namenode
  • spark内存模型

    Spark 1 6 开始使用了统一内存管理模块 UnifiedMemoryManager 并引入了堆外内存 Off heap memory 1 6之前的内存管理就不进行介绍了 spark堆内和堆外内存模型的示意图 注意 堆外内存是依赖于wo
  • Spark学习(文件读取路径)

    在不同的启动模式下 加载文件时的路径写法是不一样的 对于local模式下 默认就是读取本地文件 而在standlone或者yarn client 或者cluster模式下 默认读的都是hdfs文件系统 这几种模式下很难读取本地文件 这是很显
  • 2020-10-24 大数据面试问题

    上周面试数据开发职位主要从公司的视角讲一下记录下面试流水 1 三面技术一轮hr 面到了cto 整体来看是这一周技术含量最高信息量最大的一个 1到4轮过了4个小时 技术上的问题主要问的对数据分层的理解 1 一面自我介绍 目前团队的规模多大 2
  • Spark 任务调度机制

    1 Spark任务提交流程 Spark YARN Cluster模式下的任务提交流程 如下图所示 图YARN Cluster任务提交流程 下面的时序图清晰地说明了一个Spark应用程序从提交到运行的完整流程 图Spark任务提交时序图 提交
  • Spark Sql之dropDuplicates去重

    文章目录 算子介绍 示例 问题 解决 dropDuplicates和distinct 参考 算子介绍 dropDuplicates去重原则 按数据行的顺序保留每行数据出现的第一条 dropDuplicates 在Spark源码里面提供了以下
  • Spark 配置

    文章目录 1 Spark 配置 1 1 Spark 属性 1 1 1 动态加载Spark属性 1 1 2 查看Spark属性 1 2 环境变量 2 重新指定配置文件目录 3 继承Hadoop集群配置 4 定制的Hadoop Hive配置 1

随机推荐

  • 单片机变量所储存的变量值转化为字符

    最近做了一个设计 需要使用单片机设计一个距离采集系统 并将采集的距离大小通过语音播报出来 同时通过蓝牙传至手机端 不论是蓝牙还是语音播报都涉及到将变量中所储存的数值大小转化为字符串 编写代码环境 单片机 STM32F103C8T6 编写软件
  • qt学习笔记1:创建一个qt项目及一些基础知识

    1 新建第一个项目 New Project gt qt widges application 给项目创建名称 名称不能有中文和空格 创建路径中也不能有中文路径 不会报错但是运行时会报错 再下一步 到Kits 中文构建套件 用于选择编译套件
  • C++学习(三十三)运算符优先级

    C语言优先级 优先级 运算符 名称或含义 使用形式 结合方向 说明 1 数组下标 数组名 整型表达式 左到右 圆括号 表达式 函数名 形参表 成员选择 对象 对象 成员名 gt 成员选择 指针 对象指针 gt 成员名 2 负号运算符 算术类
  • 解决Glide在一个imageview上更换图片时会闪的问题

    Glide with MainActivity this load str msg what 1 dontAnimate placeholder iv getDrawable 原理 1 使用dontAnimate取消图片切换动画 2 使用p
  • scrapy屏幕log日志输出保存到txt文本中

    在使用scrapy框架的时候 因为scrapy在屏幕上面输出的日志一直在跑 有些错误又抓不到 无奈只能先把log日志放在文件中 慢慢进行错误日志的分析 如图所示 我们需要设置的地方只在settings py文件夹中进行设置就可以了 LOG
  • 电商系统下单锁库存java实现,【239期】面试官:如何使用Redis实现电商系统的库存扣减?...

    在日常开发中有很多地方都有类似扣减库存的操作 比如电商系统中的商品库存 抽奖系统中的奖品库存等 解决方案 使用mysql数据库 使用一个字段来存储库存 每次扣减库存去更新这个字段 还是使用数据库 但是将库存分层多份存到多条记录里面 扣减库存
  • 全国计算机等考试体系2018,2018年陕西全国计算机等级考试体系及方式

    2017年计算机等级考试已经结束 出国留学网为考生们整理了2018年陕西全国计算机等级考试体系及方式 希望能帮到大家 想了解更多资讯 请关注我们 小编会第一时间更新哦 2018年陕西全国计算机等级考试体系及方式 一 报名与考场编排 一 报名
  • 使用http 上传文件的原理

    可参考的文章有 http www cnblogs com kaixuan archive 2008 01 31 1060284 html 通过 http 协议上传文件 rfc1867协议概述 jsp 应用举例 客户端发送内容构造 1 概述
  • 如何分析AIX启动过程1

    复杂度3 5 机密度4 5 最后更新2021 05 14 AIX提供了两个帮助分析启动的工具或者模式 kernel debug boot verbose mode 前者适合单独分析某个特定的功能 模块 而后者则能帮助你全面地过一遍AIX启动
  • .net html转为pdf,.NET使用DinkToPdf将HTML转成PDF的示例代码

    0 介绍 C NET Core wrapper for wkhtmltopdf library that uses Webkit engine to convert HTML pages to PDF 最近浏览文章的时候发现DinkToPd
  • Linux 中软件包的安装常用指令

    目录 apt 常用指令 yum 常用指令 apt 常用指令 apt 与 apt get 大部分参数通用 但也会有区别 执行 apt 命令时 需要使用 root 用户的身份执行命令 如果报错 无效的操作 那可以加个sudo 试试 更新软件源
  • KMP算法详解

    目录 一 KMP是什么 二 原理 1 思路 2 预处理 3 借助nxt实现字符串匹配 总结 一 KMP是什么 烤馍片KMP算法是一种改进的字符串匹配算法 由D E Knuth J H Morris和V R Pratt提出的 因此人们称它为克
  • labelme汉化的app.py完整代码

    由于之前做了一期labelme的教程 但是汉化部分的代码有误 于是在这里贴出完整的app py代码 coding utf 8 import functools import math import os import os path as
  • scrapy错误-[scrapy.core.scraper] ERROR: Spider error processing

    一 问题 就是我的callback没得回调函数 二 然后我查看源代码 发现 三 我把解析页数的函数名设置为 def parse self response 就没保错了 能运行成功 总结 在spider的 init py文件的源代码下 设置了
  • 什么是HTML? 看这一篇就够了(附带主流IDE推荐)

    1 HTML简介 1 1 HTML是什么 百度词条 HTML称为超文本标记语言 是一种标识性的语言 它包括一系列标签 通过这些标签可以将网络上的文档格式统一 使分散的Internet资源连接为一个逻辑整体 HTML文本是由HTML命令组成的
  • 第十九篇:处理僵尸进程的两种经典方法

    前言 如果父进程没有结束 而子进程终止了 那么在父进程调用 wait 函数回收这个子进程或者父进程终止以前 这个子进程将一直是僵尸进程 本文将提供两种方法处理这个问题 方法一 父进程回收法 wait函数将使其调用者阻塞 直到其某个子进程终止
  • 伺服电机的三种控制方式与三闭环控制

    项目 FPGA双电机主从快速稳定控制实现 第一章 伺服电机的三种控制方式与三闭环控制 伺服电机的三种控制方式与三闭环控制 项目 FPGA双电机主从快速稳定控制实现 前言 一 电机控制方式 二 电机三个闭环负反馈PID控制系统 三 三闭环位置
  • GLUE基准数据集介绍

    图1 整篇文章的思维导图 一 简介 自然语言处理 NLP 主要自然语言理解 NLU 和自然语言生成 NLG 为了让NLU任务发挥最大的作用 来自纽约大学 华盛顿大学等机构创建了一个多任务的自然语言理解基准和分析平台 也就是GLUE Gene
  • 完美解决dataframe添加列,并且指定列的位置

    需求是这样的 我需要从原始表中提取几列数据 分别填入税表的人员和收入表中 原始表中只有 姓名 身份证号码 年金领取额是有效数据 但是税务局的模板表中有一大堆莫名其妙的字段不需要填写 先把原始表定义一下 把身份证字符串一下 再把需要的人员 收
  • Spark整理

    文章目录 1 概述 1 1 Spark 和 Hadoop 组成 1 2 Spark 和 Hadoop 区别 2 Spark 运行架构 2 1 基础架构 2 2 Master Worker Standalone模式 2 3 Applicati