赞
踩
Spark真实面试题总结
下面我们就分几个方面介绍两个框架的主要区别:
1)架构模型Spark Streaming 在运行时的主要角色包括:Master、Worker、Driver、Executor,Flink 在运行时主要包含:Jobmanager、Taskmanager和Slot。
2)Flink 是标准的实时处理引擎,基于事件驱动。而 Spark Streaming 是微批(Micro-Batch)的模型。
3)时间机制Spark Streaming 支持的时间机制有限,只支持处理时间。 Flink 支持了流处理程序在时间上的三个定义:处理时间、事件时间、注入时间。同时也支持 watermark 机制来处理滞后数据。
4)容错机制对于 Spark Streaming 任务,我们可以设置 checkpoint,然后假如发生故障并重启,我们可以从上次 checkpoint 之处恢复,但是这个行为只能使得数据不丢失,可能会重复处理,不能做到恰好一次处理语义。Flink 则使用两阶段提交协议来解决这个问题。
对于 Spark Streaming 任务,我们可以设置 checkpoint,然后假如发生故障并重启,我们可以从上次 checkpoint 之处恢复,但是这个行为只能使得数据不丢失,可能会重复处理,不能做到恰一次处理语义。
对于 Spark Streaming 与 kafka 结合的 direct Stream 可以自己维护 offset 到 zookeeper、kafka 或任何其它外部系统,每次提交完结果之后再提交 offset,这样故障恢复重启可以利用上次提交的 offset 恢复,保证数据不丢失。但是假如故障发生在提交结果之后、提交 offset 之前会导致数据多次处理,这个时候我们需要保证处理结果多次输出不影响正常的业务。
由此可以分析,假设要保证数据恰一次处理语义,那么结果输出和 offset 提交必须在一个事务内完成。在这里有以下两种做法:
(1)repartition(1) Spark Streaming 输出的 action 变成仅一个 partition,这样可以利用事务去做: Dstream.foreachRDD(rdd=>{ rdd.repartition(1).foreachPartition(partition=>{ // 开启事务 partition.foreach(each=>{// 提交数据 }) // 提交事务 }) })
(2)将结果和 offset 一起提交
也就是结果数据包含 offset。这样提交结果和提交 offset 就是一个操作完成,不会数据丢失,也不会重复处理。故障恢复的时候可以利用上次提交结果带的 offset。
1.构建Spark Application的运行环境(启动SparkContext)
2.SparkContext向资源管理器(可以是Standalone、Mesos或YARN)注册并申请运行Executor资源;
3.资源管理器分配Executor资源,Executor运行情况将随着心跳发送到资源管理器上;
4.SparkContext构建成DAG图,将DAG图分解成Stage,并把Taskset发送给Task Scheduler
5.Executor向SparkContext申请Task,Task Scheduler将Task发放给Executor运行,SparkContext将应用程序代码发放给Executor。
6.Task在Executor上运行,运行完毕释放所有资源。
YarnClient 运行模式介绍:
1.client向ResouceManager申请启动ApplicationMaster,同时在SparkContext初始化中创建DAGScheduler和TaskScheduler
2.ResouceManager收到请求后分配container,在合适的NodeManager中启动ApplicationMaster
3.Dirver中的SparkContext初始化完成后与ApplicationMaster建立通讯,ApplicationMaster向ResourceManager申请Application的资源
4.一旦ApplicationMaster申请到资源,便与之对应的NodeManager通讯,启动Executor,并把Executor信息反向注册给Dirver
5.Dirver分发task,并监控Executor的运行状态,负责重试失败的task
6.运行完成后,Client的SparkContext向ResourceManager申请注销并关闭自己
YarnCluster 模式介绍:
1.任务提交后会和ResourceManager通讯申请启动ApplicationMaster
2.ResourceManager分配container,在合适的NodeManager上启动ApplicationMaster,此时的ApplicationMaster就是Driver。
3.Driver启动后向ResourceManager申请Executor内存,ResourceManager接到ApplicationMaster的资源申请后会分配container,然后在合适的NodeManager上启动Executor进程,Executor进程启动后会向Driver反向注册
4.Executor全部注册完成后Driver开始执行main函数,之后执行到Action算子时,触发一个job,并根据宽依赖开始划分stage,每个stage生成对应的taskSet,之后将task分发到各个Executor上执行。
Yarn-client和Yarn-cluster的区别:
yarn-cluster模式下,Dirver运行在ApplicationMaster中,负责申请资源并监控task运行状态和重试失败的task,当用户提交了作业之后就可以关掉client,作业会继续在yarn中运行;
yarn-client模式下,Dirver运行在本地客户端,client不能离开。
Spark支持堆内内存也支持堆外内存
1)堆内内存:程序在运行时动态地申请某个大小的内存空间
2)堆外内存:直接向操作系统进行申请的内存,不受JVM控制
1)堆外内存,相比于堆内内存有几个优势:
(1)减少了垃圾回收的工作,因为垃圾回收会暂停其他的工作
(2)加快了复制的速度。因为堆内在Flush到远程时,会先序列化,然后在发送;而堆外内存本身是序列化的相当于省略掉了这个工作。
2)堆外内存,相比于堆内内存有几个缺点:
(1)堆外内存难以控制,如果内存泄漏,那么很难排查
(2)堆外内存相对来说,不适合存储很复杂的对象。一般简单的对象或者扁平化的比较适合。
1)堆内内存大小设置:–executor-memory 或 spark.executor.memory
2)在默认情况下堆外内存并不启用,spark.memory.offHeap.enabled 参数启用,并由 spark.memory.offHeap.size 参数设定堆外空间的大小。
堆内内存包括:存储(Storage)内存、执行(Execution)内存、其他内存
Storage模块负责管理Spark在计算过程中产生的数据;
执行内存主要用来存储任务在执行Shuffle时占用的内存。
静态内存管理:
在Spark最初采用的静态内存管理机制下,存储内存、执行内存和其他内存的大小在Spark应用程序运行期间均为固定的,堆内内存占比:存储内存占系统内存60%,执行内存占系统内存20%,其他内存占系统内存的20%,Storage内存和Execution内存都有预留空间,目的是防止OOM,因为Spark堆内内存大小的记录是不准确的,需要留出保险区域。
堆外的空间分配较为简单,只有存储内存和执行内存。可用的执行内存和存储内存占用的空间大小直接由参数spark.memory.storageFraction 决定,由于堆外内存占用的空间可以被精确计算,所以无需再设定保险区域。
可能出现的问题:存储内存和执行内存中的一方剩余大量的空间,而另一方却早早被占满。
统一内存管理:
Spark1.6 之后引入的统一内存管理机制,与静态内存管理的区别在于存储内存和执行内存共享同一块空间,可以动态占用对方的空闲区域。
其中最重要的优化在于动态占用机制,其规则如下:
1)设定基本的存储内存和执行内存区域(spark.storage.storageFraction参数),确定了双方各自拥有的空间的范围;
2)双方的空间都不足时,则存储到硬盘;若己方空间不足而对方空余时,可借用对方的空间;(存储空间不足是指不足以放下一个完整的Block)
3)执行内存的空间被对方占用后,可让对方将占用的部分转存到硬盘,然后”归还”借用的空间;
4)存储内存的空间被对方占用后,无法让对方”归还”,因为需要考虑 Shuffle过程中的很多因素,实现起来较为复杂。
统一内存管理的对外内存也只有存储内存和执行内存,可用的执行内存和存储内存占用的空间大小直接由参数spark.memory.storageFraction 决定。
背压机制:根据JobScheduler反馈作业的执行信息来动态调整Receiver数据接收率。主要是为了更好的协调数据接收速率与资源处理能力。启用反压机制很简单,只需要将 spark.streaming.backpressure.enabled 设置为 true 即可,这个参数的默认值为 false。
RDD 是 spark 提供的核心抽象,全称为弹性分布式数据集。所有算子都是基于 rdd 来执行的,不同的场景会有不同的 rdd 实现类,但是都可以进行互相转换。rdd 执行过程中会形成 dag 图,然后形成 lineage 保证容错性等。RDD 的数据默认存放在内存中,但是当内存资源不足时,spark 会自动将 RDD 数据写入磁盘,RDD 的弹性体现在于 RDD 上自动进行内存和磁盘之间权衡和切换的机制。
五个特性:
1.A list of partitions
有一个分片列表,就是能被切分
2.A function for computing each split
由一个函数计算每一个分片。
3.A list of dependencies on other RDDs
对其他RDD的依赖列表,依赖还具体分为宽依赖和窄依赖,但并不是所有的RDD都有依赖
4.Optionally,a Partitioner for Key-value RDDs
可选:key-value型的RDD是根据哈希来分区的,类似于mapreduce当中的paritioner接口,控制Key分到哪个reduce。
5.Optionally, a list of preferred locations to compute each split on
最优的位置去计算,也就是数据的本地性。
Flink中的Checkpoint和Spark中的Checkpoint区别主要有2点:
1:Flink中Checkpoint是持久化全局的状态 state (keyed state 或 Operator state)的快照,在Flink中增量的快照,效率比较高。spark是每个批次全量保存,Spark每次全量的快照。
2:在Flink中的Checkpoint中又仅一次语义概念和用法,而spark checkpoint没有仅一次的概念
3:其次flink的 checkpoint有三个状态后端,memery、rocksdb、hdfs,所谓的状态后端就是checkpoint的存储位置,在Spark中checkpoint的存储位置一般保存在HDFS,也可以保存至本地磁盘
但是一般情况下,checkpoint在Flink和Spark中保存的位置没太大区别,生产情况下一般存HDFS
Spark中的数据倾斜,包括Spark Streaming和Spark Sql,表现主要有下面几种:
1.Executor lost,OOM,Shuffle过程出错;
2.Driver OOM;
3.单个Executor执行时间特别久,整体任务卡在某个阶段不能结束;
4.正常运行的任务突然失败;
groupByKey;reduceByKey;aggregaByKey;join等算子引发的shuffle 操作,是导致数据倾斜可能发生的关键点所在:数据倾斜为某一个或者某几个 partition 的数据特别大,导致这几个 partition 上的计算需要耗费相当长的时间。
避免数据倾斜,解决方法,有多个方面:
1.前提是定位数据倾斜,是 OOM 了,还是任务执行缓慢,看日志,看 WebUI
2.避免不必要的 shuffle,如使用广播小表的方式,将 reduce-side-join 提升为 map-side-join
3.分拆发生数据倾斜的记录,分成几个部分进行,然后合并 join 后的结果
4.改变并行度,可能并行度太少了,导致个别 task 数据压力大
5.两阶段聚合,先局部聚合,再全局聚合
6.自定义 paritioner,分散 key 的分布,使其更加均匀
spark 非常重要的一个功能特性就是可以将 RDD 持久化在内存中。
调用 cache()和 persist()方法即可。cache()和 persist()的区别在于,cache()是 persist()的一种简化方式,cache()的底层就是调用 persist()的无参版本 persist(MEMORY_ONLY),将数据持久化到内存中。
如果需要从内存中清除缓存,可以使用 unpersist()方法。RDD 持久化是可以手动选择不同的策略的。在调用 persist()时传入对应的 StorageLevel 即可。
应用场景:当 spark 应用程序特别复杂,从初始的 RDD 开始到最后整个应用程序完成有很多的步骤,而且整个应用运行时间特别长,这种情况下就比较适合使用 checkpoint 功能。
原因:对于特别复杂的 Spark 应用,会出现某个反复使用的 RDD,即使之前持久化过但由于节点的故障导致数据丢失了,没有容错机制,所以需要重新计算一次数据。
Checkpoint 首先会调用 SparkContext 的 setCheckPointDIR()方法,设置一个容错的文件系统的目录,比如说 HDFS;然后对 RDD 调用 checkpoint()方法。之后在 RDD 所处的 job 运行结束之后,会启动一个单独的 job,来将 checkpoint 过的 RDD 数据写入之前设置的文件系统,进行高可用、容错的类持久化操作。
检查点机制是我们在 spark streaming 中用来保障容错性的主要机制,它可以使 spark streaming 阶段性的把应用数据存储到诸如 HDFS 等可靠存储系统中,以供恢复时使用。
最主要的区别在于持久化只是将数据保存在 BlockManager 中,但是 RDD 的 lineage(血缘关系,依赖关系)是不变的。但是 checkpoint 执行完之后,rdd 已经没有之前所谓的依赖 rdd 了,而只有一个强行为其设置的 checkpointRDD,checkpoint 之后 rdd 的 lineage 就改变了。
持久化的数据丢失的可能性更大,因为节点的故障会导致磁盘、内存的数据丢失。但是 checkpoint 的数据通常是保存在高可用的文件系统中,比如 HDFS 中,所以数据丢失可能性比较低。
receiver 方式:将数据拉取到 executor 中做操作,可以通过 WAL,设置了本地存储,保证数据不丢失,然后使用 Kafka 高级 API 通过 zk 来维护偏移量,保证消费数据。receiver 消费的数据偏移量是在 zk 获取的,此方式效率低,容易出现数据丢失。
receiver 方式的容错性:在默认的配置下,这种方式可能会因为底层的失败而丢失数据。如果要启用高可靠机制,让数据零丢失,就必须启用 Spark Streaming 的预写日志机制(Write Ahead Log,WAL)。该机制会同步地将接收到的 Kafka 数据写入分布式文件系统(比如 HDFS)上的预写日志中。所以,即使底层节点出现了失败,也可以使用预写日志中的数据进行恢复。
基于 Direct 方式:使用 Kafka 底层 Api,其消费者直接连接 kafka 的分区上,因为 createDirectStream 创建的 DirectKafkaInputDStream 每个 batch 所对应的 RDD 的分区与 kafka 分区一一对应,但是需要自己维护偏移量,即用即取,不会给内存造成太大的压力,效率高。
优点:简化并行读取:如果要读取多个 partition,不需要创建多个输入 DStream 然后对它们进行 union 操作。Spark 会创建跟 Kafka partition 一样多的 RDD partition,并且会并行从 Kafka 中读取数据。所以在 Kafka partition 和 RDD partition 之间,有一个一对一的映射关系。
高性能:如果要保证零数据丢失,在基于 receiver 的方式中,需要开启 WAL 机制。这种方式其实效率低下,因为数据实际上被复制了两份,Kafka 自己本身就有高可靠的机制,会对数据复制一份,而这里又会复制一份到 WAL 中。而基于 direct 的方式,不依赖 Receiver,不需要开启 WAL 机制,只要 Kafka 中作了数据的复制,那么就可以通过 Kafka 的副本进行恢复。
receiver 与和 direct 的比较:
基于 receiver 的方式,是使用 Kafka 的高阶 API 来在 ZooKeeper 中保存消费过的 offset 的。这是消费 Kafka 数据的传统方式。这种方式配合着 WAL 机制可以保证数据零丢失的高可靠性,但是却无法保证数据被处理一次且仅一次,可能会处理两次。因为 Spark 和 ZooKeeper 之间可能是不同步的。
基于 direct 的方式,使用 Kafka 的低阶 API,Spark Streaming 自己就负责追踪消费的 offset,并保存在 checkpoint 中。Spark 自己一定是同步的,因此可以保证数据是消费一次且仅消费一次。
Receiver 方式是通过 zookeeper 来连接 kafka 队列,Direct 方式是直接连接到 kafka 的节点上获取数据。
累加器和广播变量属于共享变量,累加器是只写变量,广播变量是只读变量。
共享变量是指可以在 Excutor 上来更改(累加器) 和读取(广播变量) Driver 上的数据
累加器:
用途:累加器的常见用途是在调试时对作业执行的过程中的事件进行计数。例如:统计 100 内的偶数的个数
用法:
通过调用 SparkContext 的 accumulator(initiaValue) 方法来创建累加器 ac
在 scala 中通过 += 来更改 ac(java 中通过 add 来修改)
使用 ac.value 来访问累加器的值
广播变量
2.1 用途
当多个 Executor 中的多个 Task 操作需要使用(读取)同一个很大变量时,如果我们采取常规方式把该变量发送到每一个 task 中,那么会极大地浪费性能,所以我们可以直接把该变量发送到每一个 Executor 上,Executor 上对应的 Task 可以共同访问该变量,这样就可以提高性能。
scala> val sourceRDD = sc.makeRDD(1 to 100, 3)
sourceRDD: org.apache.spark.rdd.RDD[Int] = ParallelCollectionRDD[0] at makeRDD at :24
//1. 创建累加器
scala> val accumulator = sc.accumulator(0)
//2. 修改累加器的值
scala> val test = sourceRDD.map(x => {if(x % 2 == 0) accumulator += 1})
//3. 访问累加器
scala> println(accumulator.value)
50
2.2 用法
通过 SparkContext 的 broadcast 方法创建广播变量
通过 value 来访问广播变量的值
//这里只做一个示范,就广播一个字符串,其实实际应用中广播的全是大数据,如巨大的矩阵
scala> val a = “a”
scala> val broadValue = sc.broadcast(a)
scala> val source = sc.makeRDD(1 to 10, 3)
scala> val castRDD = source.map(x => x + broadValue.value)
scala> castRDD.collect
res8: Array[String] = Array(1a, 2a, 3a, 4a, 5a, 6a, 7a, 8a, 9a, 10a)
区别:
map是对rdd中的每一个元素进行操作;
mapPartitions则是对rdd中的每个分区的迭代器进行操作
mapPartitions优点:
如果是普通的map,比如一个partition中有1万条数据。ok,那么你的function要执行和计算1万次。
使用MapPartitions操作之后,一个task仅仅会执行一次function,function一次接收所有
的partition数据。只要执行一次就可以了,性能比较高。如果在map过程中需要频繁创建额外的对象(例如将rdd中的数据通过jdbc写入数据库,map需要为每个元素创建一个链接而mapPartition为每个partition创建一个链接),则mapPartitions效率比map高的多。
SparkSql或DataFrame默认会对程序进行mapPartition的优化。
缺点:
如果是普通的map操作,一次function的执行就处理一条数据;那么如果内存不够用的情况下, 比如处理了1千条数据了,那么这个时候内存不够了,那么就可以将已经处理完的1千条数据从内存里面垃圾回收掉,或者用其他方法,腾出空间来吧。
所以说普通的map操作通常不会导致内存的OOM异常。
但是MapPartitions操作,对于大量数据来说,比如甚至一个partition,100万数据,
一次传入一个function以后,那么可能一下子内存不够,但是又没有办法去腾出内存空间来,可能就OOM,内存溢出。
1.RDD不支持sparkSQL操作,DF和DS支持sparkSQL
2.DF每一行类型固定为Row,只有通过解析才能获取值。
3.DS每一行是什么类型是不一定的,在自定义了case class之后可以很自由的获 得 每一行的信息
4.DF和DS支持方便地保存文件格式,可以直接指定
executor-cores —— 每个executor使用的内核数,默认为1,官方建议2-5个,我 们企业是4个//8
num-executors —— 启动executors的数量,默认为2//10
executor-memory —— executor内存大小,默认1G//15g
driver-cores —— driver使用内核数,默认为1
driver-memory —— driver内存大小,默认512M//30g
资源参数调优:
num-executors:设置Spark作业总共要用多少个Executor进程来执行
executor-memory:设置每个Executor进程的内存
executor-cores:设置每个Executor进程的CPU core数量
driver-memory:设置Driver进程的内存
spark.default.parallelism:设置每个stage的默认task数量
开发调优:
①避免创建重复的RDD
②尽可能复用同一个RDD
③对多次使用的RDD进行持久化
④尽量避免使用shuffle类算子
⑤使用map-side预聚合的shuffle操作
使用高性能的算子:
①使用reduceByKey/aggregateByKey替代groupByKey
②使用mapPartitions替代普通map
③使用foreachPartitions替代foreach
④使用filter之后进行coalesce操作
⑤使用repartitionAndSortWithinPartitions替代repartition与sort类操作
1.map:对RDD每个元素转换,文件中的每一行数据返回一个数组对象
2.flatMap:对RDD每个元素转换,然后再扁平化,将所有的对象合并为一个对象,会抛弃值为null的值
1.基于内存计算,减少低效的磁盘交互
2.基于DAG的高效的调度算法
3.容错机制Linege
1、Spark Core
实现Spark的基本功能,包括任务调度、内存管理、错误恢复、与存储系统交互等,以及RDD(Resilient Distributed Dataset)API的定义。
2、Spark SQL
用Spark来操作结构化数据的程序包。可以使用SQL或Hive的HQL来查询数据,并可以与RDD的操作相结合使用。
3、Spark Streaming
用来对实时数据进行流式计算的组件,Streaming中提供操作流式数据的API与RDD高度对应。Streaming与日志采集工具Flume、消息处理Kafka等可集成使用。
4、 MLib
机器学习(ML)的功能库,提供多种学习算法,包括分类、回归、聚类、协同过滤等,还提供了模型评估、数据导入等功能。
5、 GraphX
用来操作图的程序库,可以用于并行的图计算。扩展了RDD API功能,用来创建一个顶点和边都包含任意属性的有向图。支持针对图的各种操作,如图的分割subgraph、操作所有的顶点mapVertices、三角计算等。
master:管理集群和节点,不参与计算。
worker:计算节点,进程本身不参与计算,和 master 汇报。
Driver:运行程序的 main 方法,创建 spark context 对象。
spark context:控制整个 application 的生命周期,包括 dagsheduler 和 task scheduler 等组件。
client:用户提交程序的入口。
窄依赖:父 RDD 的一个分区只会被子 RDD 的一个分区依赖;
宽依赖:父 RDD 的一个分区会被子 RDD 的多个分区依赖(涉及到 shuffle)。
相同点:都是将 mapper(Spark 里是 ShuffleMapTask)的输出进行 partition,不同的 partition 送到不同的 reducer(Spark 里 reducer 可能是下一个 stage 里的 ShuffleMapTask,也可能是 ResultTask)
不同点:
MapReduce 默认是排序的,spark 默认不排序,除非使用 sortByKey 算子。
MapReduce 可以划分成 split,map()、spill、merge、shuffle、sort、reduce()等阶段,spark 没有明显的阶段划分,只有不同的 stage 和算子操作。
MR 落盘,Spark 不落盘,spark 可以解决 mr 落盘导致效率低下的问题。
spark中会导致shuffle操作的有以下几种算子:
1、repartition类的操作:比如repartition、repartitionAndSortWithinPartitions、coalesce等;
2、byKey类的操作:比如reduceByKey、groupByKey、sortByKey等;
3、join类的操作:比如join、cogroup等。
Spark streaming 是 spark core API 的一种扩展,可以用于进行大规模、高吞吐量、容错的实时数据流的处理。
它支持从多种数据源读取数据,比如 Kafka、Flume、Twitter 和 TCP Socket,并且能够使用算子比如 map、reduce、join 和 window 等来处理数据,处理后的数据可以保存到文件系统、数据库等存储中。
Spark streaming 内部的基本工作原理是:接受实时输入数据流,然后将数据拆分成 batch,比如每收集一秒的数据封装成一个 batch,然后将每个 batch 交给 spark 的计算引擎进行处理,最后会生产处一个结果数据流,其中的数据也是一个一个的 batch 组成的。
关系:
两者都是用来改变RDD的partition数量的,repartition底层调用的就是coalesce方法:coalesce(numPartitions, shuffle = true)
区别:
repartition一定会发生shuffle,coalesce 根据传入的参数来判断是否发生shuffle。
一般情况下增大rdd的partition数量使用repartition,减少partition数量时使用coalesce。
DAG(Directed Acyclic Graph 有向无环图)指的是数据转换执行的过程,有方向,无闭环(其实就是 RDD 执行的流程);
原始的 RDD 通过一系列的转换操作就形成了 DAG 有向无环图,任务执行时,可以按照 DAG 的描述,执行真正的计算(数据被操作的一个过程)。
并行计算。
一个复杂的业务逻辑如果有 shuffle,那么就意味着前面阶段产生结果后,才能执行下一个阶段,即下一个阶段的计算要依赖上一个阶段的数据。那么我们按照 shuffle 进行划分(也就是按照宽依赖就行划分),就可以将一个 DAG 划分成多个 Stage/阶段,在同一个 Stage 中,会有多个算子操作,可以形成一个 pipeline 流水线,流水线内的多个平行的分区可以并行执行。
对于窄依赖,partition 的转换处理在 stage 中完成计算,不划分(将窄依赖尽量放在在同一个 stage 中,可以实现流水线计算)。
对于宽依赖,由于有 shuffle 的存在,只能在父 RDD 处理完成后,才能开始接下来的计算,也就是说需要要划分 stage。
核心算法:回溯算法
从后往前回溯/反向解析,遇到窄依赖加入本 Stage,遇见宽依赖进行 Stage 切分。
Spark 内核会从触发 Action 操作的那个 RDD 开始从后往前推,首先会为最后一个 RDD 创建一个 Stage,然后继续倒推,如果发现对某个 RDD 是宽依赖,那么就会将宽依赖的那个 RDD 创建一个新的 Stage,那个 RDD 就是新的 Stage 的最后一个 RDD。然后依次类推,继续倒推,根据窄依赖或者宽依赖进行 Stage 的划分,直到所有的 RDD 全部遍历完成为止。
分区的动态可调是弹性的体现:
1.可以根据数据量的大小,通过repartition、coalesce算子来增加或者减少分区数,进而决定Task数的多少。
2.对应的计算资源调整,可以通过提交任务时的参数来调整,也可以设置成动态调整方式。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。