spark

一、spark的基本介绍

1.1 spark是什么

        Apache Saprk是用于大规模数据处理的统一分析引擎.

1.2 Saprk与MapReduce对比

MapReduce主要缺点:

        计算阶段数据的读写有反复的IO操作,也就是从磁盘到内存,以及内存到磁盘的过程,会导致MapReduce程序执行效率降低,同时API比较低级,实现复杂功能,需要编写代码量大.

Spark与MapReduce的区别:

  • Spark中引用了新的数据结构RDD(弹性分布式数据集),让Spark程序可以基于内存进行计算,但是MapReduce是基于磁盘来计算的
  • 内存相对磁盘来说,数据读写速度要快得多
  • Spark中如果内存不够用了,也就是数据再内存中放不下了.Spark还可以使用磁盘来存储数据.
  • Spark中是使用线程来执行的,而MapReduce中是使用进程来执行的.线程的创建速度比进程会快很多,同个进程里面的程序可以共享数据,但是进程间很难共享数据.

    1.3 Spark优势及特点 

  • 高效性

    • 计算速度快

      • 提供了一个全新的数据结构RDD(弹性分布式数据集)。整个计算操作,基于内存计算。当内存不足的时候,可以放置到磁盘上。整个流程是基于DAG(有向无环图)执行方案。

      • Task线程完成计算任务执行

  • 易用性

    • 支持多种语言开发 (Python,SQL,Java,Scala,R),降低了学习难度

  • 通用性

    • 在 Spark 的基础上,Spark 还提供了包括Spark SQL、Spark Streaming、MLlib 及GraphX在内的多个工具库,我们可以在一个应用中无缝地使用这些工具库。

  • 兼容性(任何地方运行)

    • 支持三方工具接入

      • 存储工具

        • hdfs

        • kafka

        • hbase

      • 资源调度

        • yarn

        • Kubernetes(K8s容器)

        • standalone(spark自带的)

      • 高可用

        • zookeeper

    • 支持多种操作系统

      • Linux

      • windows

      • Mac

1.3.1磁盘的I/O操作是什么?

        磁盘的I/O操作指的是计算机系统与磁盘存储设备之间进行数据输入和输出的过程,磁盘I/O操作是计算机系统重要的数据访问方式之一,用于读取和写入存储在磁盘上的数据.

        在计算机系统中,磁盘是一种非易失性的存储介质,可以长期保存大量的数据,磁盘I/O操作通过读取和写入磁盘的数据块(通常以扇区为单位)来实现数据的读取和写入.

磁盘的I/O操作通常涉及一下步骤:

        1.发送命令:计算机系统通过控制器想磁盘发送相应的命令,如读取,写入或其他操作命令.

        2.寻道操作:磁盘根据命令定位到相应的磁道,这个过程称为寻道.寻道操作需要将磁头移动到目标磁道上,这是一个机械运动的过程,会引起一定的延迟.

        3.旋转等待:当磁头定位到目标磁道后,磁盘需要等待目标数据所在的扇区旋转到磁头下方,这个过程称为旋转等待.旋转等待的时间取决于磁盘的转速.

        4.数据传输:一旦目标扇区旋转到磁头下方,磁盘开始将数据从磁盘传输到计算机系统(读取操作),或将数据从计算机系统传输到磁盘(写入操作).

        5.完成操作:数据传输完成后,磁盘返回相应的状态信息给计算机系统,表示操作已经完成.

1.3.2 优秀的数据模型和丰富的计算抽象

        首先看看MapReduce,它提供对数据访问和计算的抽象,但是对应数据复用就是简单的将中间数据写入到一个稳定的文件系统中(例如HDFS),所以产生数据的复制备份,磁盘的I/O以及数据的序列化,所以在遇到需要在多个计算复用中间结果的操作效率就会非常低,而这类操作是非常常见的,例如迭代式运算,交互式数据挖掘,图设计等. 

因此APPLab提出一个新的模型.叫RDD

1.3.3.Spark主备切换机制原理

        Spark Master主备切换主要有两种机制,之中是基于文件系统,一种是基于Zookeeper.基于文件系统的主备切换机制需要在Active Master挂掉后手动切换到Standby Master上,而基于Zookeeper的主备切换机制可以实现自动切换Master。

二.RDD

2.1RDD的概念

        RDD是一个可以容错且并行的数据结构(其实也可以理解成分布式的集合,操作起来和操作本地集合一样简单),它可以让用户显示的将中间结果数据集保持到内存中,并且通过控制数据集的分区来达到数据存放处理最优化.同时RDD也提供丰富的API来操作数据集.

详解:

        在Apache Spark中,RDD(弹性分布式数据集)将中间结果数据集保持在内存中。具体来说,它们通常被存储在计算节点的JVM堆内存中。每个节点上的内存由Spark管理,并且可以根据应用程序的需求进行动态调整。

        RDD的内存存储提供了高速的数据访问和转换操作,从而加快了Spark应用程序的执行速度。通过将中间结果保存在内存中,避免了磁盘I/O的开销,提高了数据处理的效率。

需要注意的是,尽管RDD可以将数据集存储在内存中,但这并不意味着所有的数据都会一直保留在内存中。RDD使用一种称为"弹性"的机制,根据内存压力和数据访问模式来决定哪些数据应该被保留在内存中,哪些数据可以被临时地或永久地释放出来。

        此外,Spark还提供了其他类型的数据结构,如DataFrame和Dataset,它们也可以利用内存存储来提高查询和操作的性能。无论是RDD、DataFrame还是Dataset,内存存储的原理都是类似的,都是将中间结果数据集存储在计算节点的内存中以提高性能

        然而在传统的MapReduce框架中,中间结果数据集通常被存储在分布式文件系统如Hadoop的HDFS)中。这是因为MapReduce是一种批处理模型,需要将中间结果持久化到磁盘上以便后续的阶段和任务可以访问。

2.2 RDD源码详解

RDD单词拆解:

  • Resilient :它是弹性的,RDD 里面的中的数据可以保存在内存中或者磁盘里面;
  • Distributed : 它里面的元素是分布式存储的,可以用于分布式计算;
  • Dataset它是一个集合,可以存放很多元素。

RDD源码:

  •  A list of partitions :一组分片(Partition)/一个分区(Partition)列表,即数据集的基本组成单位。对于 RDD 来说,每个分片都会被一个计算任务处理,分片数决定并行度。用户可以在创建 RDD 时指定 RDD 的分片个数,如果没有指定,那么就会采用默认值。
  • A function for computing each split :一个函数会被作用在每一个分区。Spark 中 RDD 的计算是以分片为单位的,compute 函数会被作用到每个分区上。
  • A list of dependencies on other RDDs :一个 RDD 会依赖于其他多个 RDD。RDD 的每次转换都会生成一个新的 RDD,所以 RDD 之间就会形成类似于流水线一样的前后依赖关系。在部分分区数据丢失时,Spark 可以通过这个依赖关系重新计算丢失的分区数据,而不是对 RDD 的所有分区进行重新计算。(Spark 的容错机制)
  • Optionally, a Partitioner for key-value RDDs (e.g. to say that the RDD is hash-partitioned):可选项,对于 KV 类型的 RDD 会有一个 Partitioner,即 RDD 的分区函数,默认为 HashPartitioner。
  • Optionally, a list of preferred locations to compute each split on (e.g. block locations for an HDFS file):可选项,一个列表,存储存取每个 Partition 的优先位置(preferred location)。对于一个 HDFS 文件来说,这个列表保存的就是每个 Partition 所在的块的位置。按照"移动数据不如移动计算"的理念,Spark 在进行任务调度的时候,会尽可能选择那些存有数据的 worker 节点来进行任务计算。

总结RDD 是一个数据集的表示,不仅表示了数据集,还表示了这个数据集从哪来,如何计算,主要属性包括:

  • 分区列表
  • 计算函数
  • 依赖关系
  • 分区函数(默认是 hash)
  • 最佳位置

 2.3 RDD 算子

RDD 的算子分为两类:

  • Transformation转换操作:返回一个新的 RDD
  • Action动作操作:返回值不是 RDD(无返回值或返回其他的)

注意:

        1.RDD不实际存储真正要的计算的数据,而是记录了数据的位置在哪里,数据的转换关系(调用了什么方法,传入什么函数).

        2.RDD中的所有转换都是惰性求值/延迟执行的,也就是说并不会直接计算,只有当发送一个要求返回结果给DriverAction动作时,这些转换才会真正运行.

        3.之所以使用惰性求值/延迟执行,是因为这样可以在Action时对RDD操作形成DAG有向无环图进行Stage的划分和并行优化,这种设计让Spark更加有效率的运行.

        在Spark中,Action是一种触发计算的操作。当我们对RDD应用一个Action操作时,Spark会开始执行计算,并将结果返回给驱动程序(Driver)。Action操作通常会导致Spark执行一系列的转换操作(Transformation)以及其他必要的计算步骤,最终生成一个可供驱动程序使用的结果。例如,常见的Action操作包括`count()`(计算RDD中元素的数量)、`collect()`(将RDD中的所有元素收集到驱动程序中)、`reduce()`(对RDD中的元素进行归约操作)等。

        通过触发Action操作,我们可以获取到RDD计算的结果,并在驱动程序中进一步处理或输出。在执行Action操作之前,所有的转换操作都只是记录了数据的转换关系,而不会实际进行计算。这种惰性求值的特性使得Spark能够优化计算流程,并提高运行效率。

转换算子

  • Transformations 类算子叫做转换算子(本质就是函数), Transformations 算子是延迟执行,也叫懒加载执行。

常见Transformations类算子:

  • filter:过滤符合条件的记录数, true 保留, false 过滤掉。
  • map:将一个 RDD 中的每个数据项,通过 map 中的函数映射变为一个新的元素。特点:输入 一条,输出一条数据。
  • flatMap:先 map 后 flat 。与 map 类似,每个输入项可以映射为0到多个输出项。
  • sample:随机抽样算子,根据传进去的小数按比例进行有返回或者无返回的抽样。
  • reduceByKey 将相同的 Key 根据相应的逻辑进行处理。
  • sortByKey / sortBy 作用在 K,V格式的RDD 上,对 key 进行升序或者降序排序。

行动算子

  • Action 类算子叫做行动算子, Action 类算子是触发执行。
  • 一个application 应用程序中有几个 Action 类算子执行,就有几个 job 运行。

常见Action 类算子

  • count :返回数据集中的元素数。会在结果计算完成后回收到 Driver 端。
  • take(n) :返回一个包含数据集前 n 个元素的集合。
  • first:效果等同于 take(1) ,返回数据集中的第一个元素。
  • foreach:循环遍历数据集中的每个元素,运行相应的逻辑。
  • collect:将计算结果回收到 Driver 端。

控制算子
        将RDD 持久化,持久化的单位是 partition 

        控制算子有三种: cache , persist , checkpoint 。 cache 和 persist 都是懒执行的。必须有一个 action 类算子触发执行

cache

  •  cache默认将Rdd的数据持久化到内存中.cache是懒执行
  • cache()=persist() = persist(StorageLevel.Memory_Only)

详解:

        `cache()`, `persist()`, 和 `persist(StorageLevel.Memory_Only)` 是在 Apache Spark 中用于缓存数据的方法。

        这些方法都用于将数据集缓存在内存中,以便在后续计算过程中可以快速访问数据而不必每次都重新计算或从磁盘读取。它们的作用是相同的,即将数据集缓存在内存中。

默认情况下,`cache()` 和 `persist()` 方法使用的存储级别是 `StorageLevel.MEMORY_ONLY`,即将数据存储在内存中。这意味着数据会尽可能地保留在内存中,如果内存不足,部分数据可能会被删除以腾出空间。

        因此,`cache() = persist() = persist(StorageLevel.Memory_Only)` 表示这三个方法都具有相同的效果,即将数据缓存到内存中。

  • rdd cache().count() 返回的不是持久化的RDD,而是一个数值

persist

        可以指定持久化级别,最常用的是memory_only和memory_and_disk

详解:  

  1. MEMORY_ONLY:这是默认的持久化级别。它将数据集存储在内存中,并尽量避免将数据写入磁盘。如果内存不足以容纳整个数据集,那么某些分区的数据可能会被丢弃,并且需要重新计算。这种持久化级别适用于对性能要求较高,但数据可以轻易重建的场景。

  2. MEMORY_AND_DISK:这种持久化级别将数据集存储在内存中,同时也可以将数据溢出到磁盘上。如果内存不足以容纳整个数据集,那么会将一部分数据写入磁盘,以便节省内存空间。当需要读取数据时,Spark首先尝试从内存中获取数据,如果找不到,则从磁盘读取数据。这种持久化级别适用于数据集较大,但仍然可以放在内存中的情况。

除了上述两个常用的持久化级别之外,还有其他几种持久化级别可供选择,如:

  • MEMORY_ONLY_SER:将数据集以序列化的形式存储在内存中,可以节省内存空间,但读取速度较慢。

  • MEMORY_AND_DISK_SER:类似于MEMORY_AND_DISK,但是将数据以序列化的形式存储在内存和磁盘上。

  • DISK_ONLY:将数据集仅存储在磁盘上,适用于数据集过大,无法放入内存的情况。

  • OFF_HEAP:将数据集存储在堆外内存中,可以减轻GC压力,但需要更多的内存。


checkpoint

        checkpoint将RDD持久化到磁盘,还可以切断RDD之间的依赖关系,也是懒执行

        checkpoint 算子不仅能将 RDD 持久化到磁盘,还能切断 RDD 之间的依赖关系

详解:

1.        `checkpoint()`是在 Apache Spark 中用于将RDD持久化到磁盘的方法。它可以将RDD的计算结果写入磁盘,以便在后续的计算中能够快速访问数据,而不必重新计算。

除了持久化数据到磁盘外,`checkpoint()`还有另一个重要的作用:它可以切断RDD之间的依赖关系。在Spark中,RDD通常会被计算图中的其他RDD所依赖。当执行`checkpoint()`时,Spark会将该RDD的计算结果写入磁盘,并将其标记为已经checkpointed。这样一来,后续的计算过程中,如果需要使用这个RDD的数据,Spark就可以直接从磁盘读取,而不需要再依赖之前的计算图。

        至于"懒执行",它是Spark的一个核心特性。在Spark中,RDD操作是惰性执行的,也就是说,只有在真正需要计算结果时才会执行。在执行`checkpoint()`之前,所有的RDD操作都只是构建了一个计算图,并没有实际执行计算。当我们调用一个触发动作(例如`count()`、`collect()`等)时,Spark才会按需计算并返回结果。

所以,`checkpoint()`的懒执行意味着它只有在触发动作执行时才会将RDD的计算结果写入磁盘,而不是立即执行。这种惰性执行的特性使得Spark能够进行优化和延迟计算,以提高整体性能。

2.        执行`checkpoint()`方法后,可以通过之前的RDD来访问已经持久化到磁盘上的数据。当一个RDD被checkpointed后,它将不再依赖于之前的计算图,而是从磁盘读取已经计算好的结果。

        通过调用`checkpoint()`方法,你可以在需要时随时访问或重新使用已经checkpointed的RDD,而无需重新计算整个计算图。这对于需要多次使用相同结果的情况下特别有用,可以提高计算效率。

        需要注意的是,`checkpoint()`方法会触发真正的计算操作,并将RDD的计算结果写入磁盘。因此,在执行`checkpoint()`之前,请确保你的集群配置和磁盘空间足够支持该操作。另外,一旦RDD被checkpointed,它将无法撤销,也无法再继续修改或重新计算。

checkpoint和持久化的区别 

在 Apache Spark 中,checkpoint 和持久化(或称缓存)都是数据持久化的方式,但它们之间存在一些关键区别。

  • 1. 存储位置:持久化的数据通常存储在内存中(也可以选择存储在磁盘上),而 checkpoint 的数据则需要存储在一个稳定的、分布式的文件系统,如 HDFS 或 S3 上。
  • 2. 线程:持久化操作是在计算节点本地进行的,而 checkpoint 操作则需要将数据写入到远程存储系统,这可能会涉及网络通信。
  • 3. lineage 信息:持久化保留了 RDD 的 lineage 信息,因此如果持久化的数据丢失,可以通过 lineage 重新计算。而 checkpoint 会切断 lineage,创建一个新的 RDD,其 lineage 信息只包含一个文件路径,指向存储在文件系统中的数据。
  • 4. 使用场景:由于性能考虑,通常情况下更倾向于使用持久化。但是,在执行很长的转换链时,为了避免在出现问题时重新计算整个链,或者为了处理像迭代算法或图计算这样的复杂计算,我们可能会选择使用 checkpoint。
  • 5. 触发时间:持久化操作是懒加载的,即只有当 action 操作触发时才会真正进行。而 checkpoint 操作在被调用后就会立即执行。

checkpoint 和持久化是两种不同的数据持久化机制,选择哪种机制取决于特定的应用需求和环境。

        在Apache Spark中,当执行一个action操作时,Spark会通过lineage信息计算出需要的结果。如果RDD已经被持久化,那么Spark可以直接使用存储的数据,而不需要重新计算整个链。然而,如果持久化的数据因为某些原因(如节点故障)丢失了,那么Spark还是可以通过lineage信息来重新计算丢失的部分。

        另一方面,checkpoint是一种将数据保存到可靠存储系统(如HDFS)的方法,同时它也会切断lineage信息。这样做的好处是,可以避免在长线路图中由于节点故障导致的大量重新计算,使得恢复更快;并且在处理像迭代算法或图计算这样的复杂计算时,可以防止lineage图过大导致的问题。

 checkpoint执行原理:

  • 当RDD的job执行完毕后,会从finalRDD从后往前回溯.
  • 当回溯到某个RDD调用了checkpoint方法,会对当前的RDD做一个标记.
  • Spark框架会自动启动一个新的job,重新计算这个RDD的数据,将数据持久化到Checkpoint目录中.

使用Checkpoint时常用优化手段:

  • 对RDD执行Checkpoint之前,最好对RDD先执行cache
  • 这样新启动的job只需要将内存中的数据拷贝到Checkpoint目录中就可以,省去重新计算这一步

Checkpoint优化手段详解:

        当我们对一个RDD执行`checkpoint()`方法时,Spark会将RDD的数据持久化到磁盘,并切断RDD之间的依赖关系。这意味着,即使在重新启动应用程序后,Spark也不需要从头开始计算该RDD,而是可以直接从磁盘中读取数据。

        然而,如果我们在执行`checkpoint()`之前没有对RDD执行`cache()`方法,那么在重新启动应用程序后,Spark仍然需要重新计算该RDD,并将结果写入磁盘。这是因为RDD的数据在内存中不存在,而且RDD之间的依赖关系也没有被切断。

通过在执行`cache()`之后再执行`checkpoint()`,我们可以将RDD的数据缓存在内存中,以提高计算性能。在重新启动应用程序后,Spark只需将内存中的数据拷贝到Checkpoint目录中,而不需要重新计算RDD。

        所以,确保在对RDD执行`checkpoint()`之前先执行`cache()`方法,可以避免重新计算RDD的开销,并提高应用程序的性能。

RDD的血缘

        RDD可以从本地集合并行化、从外部文件系统、其他RDD转化得到,能从其他RDD通过Transformation创建新的RDD的原因是RDD之间有依赖关系,代表了RDD之间的依赖关系,即血缘,RDD和他依赖的父RDD有两种不同的类型,即宽依赖和窄依赖。

        划分宽依赖和窄依赖的关键点在:分区的依赖关系。也就是说父RDD的一个分区的数据,是给子RDD的一个分区,还是给子RDD的所有分区。父RDD的每一个分区,是被一个子RDD的一个分区依赖(一对一或者多对一),就是窄依赖。如果父RDD的每一个分区,是被子RDD的各个分区所依赖(一对多),就是宽依赖。一旦有宽依赖发生,就意味着会发生数据的shuffle。发生了shuffle也就意味着生成了新的stage。

宽依赖函数,窄依赖函数分别有哪些?

窄依赖的函数有:map, filter, union, join(父RDD是hash-partitioned ), mapPartitions, mapValues
宽依赖的函数有:groupByKey, join(父RDD不是hash-partitioned ), partitionBy
 

三、PySpark程序与Spark交互流程

client on Spark集群

1.用户提交pyspark应用:用户编写一个pyspark应用,然后使用Spark-submit命令(通常配合--master选项来指定集群地址)提交这个应用.

2.启动Driver程序:pyspark应用在一个叫Driver Program的进程中启动.这个Driver Program包含了应用的main()函数,并且定义了集群分布式数据集,(RDDs或DataFrames)以及对他们的转换操作.

创建SparkContext:Driver程序尝试初始化一个SparkContext.SparkContext是Spark所有功能的入口点.在pyspark中,SparkContext会自动创建一个javaSparkContext来与java的Spark代码进行交互.

任务调度:一旦SparkContext被成功初始化,他会尝试获取Executor资源,Executor是在工作节点(Worker Node)上运行的一种独立的进程,用于运行任务.每个应该都有自己的Executor进程.

任务执行:Driver程序将计算逻辑发送给Executor.在pyspark中,这涉及到将python代码序列化并发送到每个Executor.每个Executor都运行一个python解释器,用于执行这些代码.

结果返回:任务完成后,结果返回给Driver程序,在pyspark中,这涉及到将结果从java序列化为python对象.

Yarn-Client 执行流程

  • 用户在客户端(Client)机器上提交Spark应用程序。
  • 客户端提交一个Application,在客户端启动一个Driver进程.
  • 应用程序启动后会向ResourceManage发送请求,请求启动ApplicationMaster.
  • ResourceManage收到请求后,随机选择一台NodeManager启动ApplicationMaster.
  • ApplicationMaster启动后,向ResourceManage请求启动一批Container(资源),用于启动Executor
  • 然后ResourceManage返回给ApplicationMaster一批NodeManager节点.用于启动Executor.
  • ApplicationMaster向NodeManager发送命令启动Executor.
  • Executor启动后,会反向注册给Driver,Driver发送task到Executor,执行情况和结果返回给Driver端.

yarn-cluster 执行流程

  • 应用程序启动后,Driver程序会在集群中某个节点上运行,通常由YARN的ApplicationMaster承担。
  • 客户端提交Application应用程序,发送请求到ResourceManager,请求启动ApplicationMaster.
  • ResourceManager收到请求后随机在一台NodeManager上启动ApplicantionMaster.(相当于Driver端)
  • ApplicantionMaster启动后,发送请求到ResourceManager,请求一批container节点用于启动Executor.
  • ResourceManager返回一批container节点给ApplicantionMaster.
  • ApplicantionMaster连接到NodeManager,发送请求到NodeManager启动Executor.
  • Executor反向注册到ApplicantionMaster所在的节点的Driver.然后Driver发送task到Executor.

四、窄依赖和宽依赖

RDD之间有一系列的依赖关系,依赖关系又分为窄依赖宽依赖

  •  窄依赖
    • 父RDd和子RDD的Partition之间的关系是一对一的.或者父RDD和子RDD的Partition关系是多对一的,不会有Shuffle的产生.
  • 宽依赖
    • 父RDD与子RDD的Partition之间的关系是一对多,会产生Shuffle的产生 

五、Stage

        Spark任务会根据RDD之间的依赖关系,形成一个DAG有向无环图,DAG提交给DAGSchedule,DAGSchedule会把DAG划分成相互依赖的多个Stage,划分Stage的依据就是RDD之间的宽窄依赖,,遇到宽依赖就划分stage,每个stage包含一个或者多个task任务.然后将这些task以taskSet的形式提交给TaskSchedule运行.

stage是由一组并行的task组成.

stage任务执行的流程

  • DAG创建:根据RDD之间的依赖关系,Spark会构建一个DAG有向无环图,每个节点表示一个RDD操作.例如转换或者动作.
  • DAG划分为阶段:DAGScheduler将DAG划分为多个阶段,其中每个阶段都包含一组相互依赖的RDD操作.划分的已经是RDD之间的宽依赖和窄依赖.宽依赖表示需要再不同的分区进行数据Shuffle,而窄依赖表示只需要在同一个分区内进行计算.
  • 阶段划分成任务集:每个阶段划分为一个或者多个任务集(TaskSet),其中每个任务集包含一个或者多个任务,任务集是具有相同的计算逻辑和依赖关系的任务的集合.
  • 任务调度:TaskScheduler接收到任务集后,根据可用的资源和调度策略,将任务分配给集中的执行器节点.执行器节点负责运行任务,并将结果返回给驱动程序..
  • 任务执行:执行器节点接收到任务后,会在独立的JVM进程中执行任务代码,任务执行过程中,数据从父RDD传递到子RDD,并进行计算和转换.
  • 返回结果:当任务完成后,执行器节点将结果返回给驱动程序,结果可以是RDD.DataFrame或者其他计算结果.

 stage切割规则

切割规则:从后往前,遇到宽依赖就切割 stage 。

从后向前推理,遇到宽依赖就断开,遇到窄依赖就把当前的RDD加入到Stage中;
每个Stage里面的Task的数量是由该Stage中最后一个RDD的Partition数量决定的;
最后一个Stage里面的任务的类型是ResultTask,前面所有其他Stage里面的任务类型都是ShuffleMapTask;
代表当前Stage的算子一定是该Stage的最后一个计算步骤;

stage计算模式

  • pipeline 管道计算模式, pipeline 只是一种计算思想、模式。
  • 在spark中pipeline是一个partition对应一个partition,所以在stage内部只有窄依赖。
  • 数据一直在管道里面什么时候数据会落地?
  • 对RDD 进行持久化( cache , persist )。
  • shuffle write 的时候。
  • Stage 的 task 并行度是由 stage 的最后一个 RDD 的分区数来决定的 。
  • 如何改变 RDD 的分区数?
  • reduceByKey(XXX,3)、GroupByKey(4)、sc.textFile(path,numpartition)
  • 使用算子时传递 分区num参数 就是分区 partition 的数量。

 六、SparkShuffle

        reduceByKey会将上个RDD中的每个key对应的所有value聚合成一个value,然后生成一个新的RDD,元素类型是<key,value>对的形式,这样每一个key对应一个聚合起来的value.

问题:

  •         聚合之前,每个key对应的value不一定都在一个Partition中,也不太可能在同个节点上,因为RDD是分布式的弹性数据集,RDD的Partition极有可能分布在各个节点上.
  • 如何聚合?
  •         Shuffle Write:上一个stage的每个map task就必须保证将自己处理的当前分区的数据相同的key写入一个分区文件中,肯会写入多个不同的分区文件中.
  •         Shuffle Read:reducer task就会从上一个stage的所有task所在的机器上寻找属于自己的那些分区文件,这样就可以保证一个key所对应的value都会汇聚到同一个节点上去处理聚合.

  七、Spark资源调度和任务调度

启动集群后, Worker 节点会向 Master 节点汇报资源情况, Master 掌握了集群资源情况。 当 Spark 提交一个 Application 后,根据 RDD 之间的依赖关系将 Application 形成一个 DAG 有向 无环图。 任务提交后, Spark 会在 Driver 端创建两个对象: DAGScheduler 和 TaskScheduler ,DAGScheduler 是任务调度的高层调度器,是一个对象。DAGScheduler 的主要作用就是将 DAG 根据 RDD 之间的宽窄依赖关系划分为一个个的 Stage ,然后将这些 Stage 以 TaskSet 的形式提交给 TaskScheduler ( TaskScheduler 是任务调度的低层 调度器,这里 TaskSet 其实就是一个集合,里面封装的就是一个个的 task 任务,也就是 stage 中 的并行的 task 任务)。TaskSchedule 会遍历 TaskSet 集合,拿到每个 task 后会将 task 发送到 Executor 中去执行(其 实就是发送到 Executor 中的线程池 ThreadPool 去执行)。task 在 Executor 线程池中的运行情况会向 TaskScheduler 反馈,当 task 执行失败时,则由 TaskScheduler 负责重试,将 task 重新发送给 Executor 去执行,默认重试3次。如果重试3次依 然失败,那么这个 task 所在的 stage 就失败了。stage 失败了则由 DAGScheduler 来负责重试,重新发送 TaskSet 到 TaskScheduler , Stage 默认重试4次。如果重试4次以后依然失败,那么这个 job 就失败了。 job 失败了, Application 就 失败了。TaskScheduler 不仅能重试失败的 task ,还会重试 straggling (落后,缓慢) task ( 也就是执 行速度比其他task慢太多的task )。如果有运行缓慢的 task 那么 TaskScheduler 会启动一个新的task 来与这个运行缓慢的 task 执行相同的处理逻辑。两个 task 哪个先执行完,就以哪个 task 的执行结果为准。这就是 Spark 的推测执行机制。在 Spark 中推测执行默认是关闭的。推测执行 可以通过 spark.speculation 属性来配置。
 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值