Spark Knowledge NO.1
1. spark中的RDD是什么,有哪些特性?
答:RDD(Resilient Distributed Dataset)叫做分布式数据集,是spark中最基本的数据抽象,它代表一个不可变,可分区,里面的元素可以并行计算的集合
Resilient:表示弹性的,弹性表示
Dataset:就是一个集合,用于存放数据的
Destributed:分布式,可以并行在集群计算
1.RDD中的数据可以存储在内存或者磁盘中;
2.RDD中的分区是可以改变的;
五大特性:
- A list of partitions:一个分区列表,RDD中的数据都存储在一个分区列表中
- A function for computing each split:作用在每一个分区中的函数
- A list of dependencies on other RDDs:一个RDD依赖于其他多个RDD,这个点很重要,RDD的容错机制就是依据这个特性而来的
- Optionally, a Partitioner for key-value RDDs(eg:to say that the RDD is hash-partitioned):可选的,针对于kv类型的RDD才有这个特性,作用是决定了数据的来源以及数据处理后的去向
- 可选项,数据本地性,数据位置最优
2. 概述一下spark中的常用算子区别(map, mapPartitions, foreach, foreachPatition)
答:map:用于遍历RDD,将函数应用于每一个元素,返回新的RDD(transformation算子)
foreach:用于遍历RDD,将函数应用于每一个元素,无返回值(action算子)
mapPatitions:用于遍历操作RDD中的每一个分区,返回生成一个新的RDD(transformation算子)
foreachPatition:用于遍历操作RDD中的每一个分区,无返回值(action算子)
总结:一般使用mapPatitions和foreachPatition算子比map和foreach更加高效,推荐使用
3. 谈谈spark中的宽窄依赖:
图中左边是宽依赖,父RDD的4号分区数据划分到子RDD的多个分区(一分区对多分区),这就表明有shuffle过程,父分区数据经过shuffle过程的hash分区器(也可自定义分区器)划分到子RDD。例如GroupByKey,reduceByKey,join,sortByKey等操作。
图右边是窄依赖,父RDD的每个分区的数据直接到子RDD的对应一个分区(一分区对一分区),例如1号到5号分区的数据都只进入到子RDD的一个分区,这个过程没有shuffle。Spark中Stage的划分就是通过shuffle来划分。(shuffle可理解为数据的从原分区打乱重组到新的分区)如:map,filter
Spark基于lineage的容错性是指,如果一个RDD出错,那么可以从它的所有父RDD重新计算所得,如果一个RDD仅有一个父RDD(即窄依赖),那么这种重新计算的代价会非常小。
Spark基于Checkpoint(物化)的容错机制何解?在上图中,宽依赖得到的结果(经历过Shuffle过程)是很昂贵的,因此,Spark将此结果物化到磁盘上了,以备后面使用
对于join操作有两种情况,如果join操作的每个partition 仅仅和已知的Partition进行join,此时的join操作就是窄依赖;其他情况的join操作就是宽依赖;因为是确定的Partition数量的依赖关系,所以就是窄依赖,得出一个推论,窄依赖不仅包含一对一的窄依赖,还包含一对固定个数的窄依赖(也就是说对父RDD的依赖的Partition的数量不会随着RDD数据规模的改变而改变)
4. spark中如何划分stage:
答:概念:Spark任务会根据RDD之间的依赖关系,形成一个DAG有向无环图,DAG会提交给DAGScheduler,DAGScheduler会把DAG划分相互依赖的多个stage,划分依据就是宽窄依赖,遇到宽依赖就划分stage,每个stage包含一个或多个task,然后将这些task以taskSet的形式提交给TaskScheduler运行,stage是由一组并行的task组成。
-
一个 job,就是由一个 rdd 的 action 触发的动作,可以简单的理解为,当你需要执行一个 rdd 的 action 的时候,会生成一个 job。
-
stage : stage 是一个 job 的组成单位,就是说,一个 job 会被切分成 1 个或 1 个以上的 stage,然后各个 stage 会按照执行顺序依次执行。
-
task :即 stage 下的一个任务执行单元,一般来说,一个 rdd 有多少个partition,就会有多少个 task,因为每一个 task 只是处理一个partition 上的数据。
-
stage 的划分标准就是宽依赖:何时产生宽依赖就会产生一个新的stage,例如reduceByKey,groupByKey,join的算子,会导致宽依赖的产生;
-
切割规则:从后往前,遇到宽依赖就切割stage;
-
每个Stage里面的Task的数量是由该Stage中最后 一个RDD的Partition数量决定的;
-
最后一个Stage里面的任务的类型是ResultTask,前面所有其他Stage里面的任务类型都是ShuffleMapTask;
-
代表当前Stage的算子一定是该Stage的最后一个计算步骤;
图解:
5.计算格式:pipeline管道计算模式,piepeline只是一种计算思想,一种模式。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1Q9MMWiR-1579501612033)(C:\Users\sqian\Documents\Others\images\spark003.png)]
通过上图我们可以看到,RDD_C、RDD_D、RDD_E、RDD_F都是窄依赖关系,所以这个将他们划分为Stage2,Stage1和Stage3同理。而每一个Stage其实就是一并并行的任务,就像上图,RDD_C、RDD_D、RDD_F之间有两个并行的task0和task1;RDD_E和RDD_F之间有两个并行的task2,task3,pipeline并行计算就是基于这些task的。
由此可以看出,在spark中pipeline是一个partition对应一个partition,所以在stage内部只有窄依赖,stage与stage之间是宽依赖。(pipeline详解)
spark的pipeline管道计算模式相当于执行了一个高阶函数,也就是说来一条数据然后计算一条数据,会把所有的逻辑走完,然后落地,而MapReduce是1+1=2,2+1=3这样的计算模式,也就是计算完落地,然后再计算,然后再落地到磁盘或者内存,最后数据是落在计算节点上,按reduce的hash分区落地。管道计算模式完全基于内存计算,所以比MapReduce快的原因。
管道中的RDD会在shuffle write的时候,对RDD进行持久化的时候。
优化
stage的task的并行度是由stage的最后一个RDD的分区数来决定的,一般来说,一个partition对应一个task,但最后reduce的时候可以手动改变reduce的个数,也就是改变最后一个RDD的分区数,也就改变了并行度。例如:reduceByKey(+,3)
总结:提高stage的并行度:reduceByKey(+,patition的个数) ,join(+,patition的个数)
6. DAGScheduler 分析:
答:概述:是一个面向stage 的调度器;
主要入参有:dagScheduler.runJob(rdd, cleanedFunc, partitions, callSite, allowLocal,resultHandler, localProperties.get)
rdd: final RDD;
cleanedFunc: 计算每个分区的函数;
resultHander: 结果侦听器;
主要功能:
1. 接受用户提交的job;
2. 将job根据类型划分为不同的stage,记录那些RDD,stage被物化,并在每一个stage内产生一系列的task,并封装成taskset;
3. 决定每个task的最佳位置,任务在数据所在节点上运行,并结合当前的缓存情况,将taskSet提交给TaskScheduler;
4. 重新提交shuffle输出丢失的stage给taskScheduler;
注:一个stage内部的错误不是由shuffle输出丢失造成的,DAGScheduler是不管的,由TaskScheduler负责尝试重新提交task执行。
7.Job的生成:
答:一旦driver程序中出现action,就会生成一个job,比如:count等,向DAGScheduler提交job,如果driver程序后面还有action,那么其他action也会对应生成相应的job,所以,driver端有多少action就会提交多少job,这可能就是为什么spark将driver程序称为application而不是job 的原因。每一个job可能会包含一个或者多个stage,最后一个stage生成result,在提交job 的过程中,DAGScheduler会首先从后往前划分stage,划分的标准就是宽依赖,一旦遇到宽依赖就划分,然后先提交没有父阶段的stage们,并在提交过程中,计算该stage的task数目以及类型,并提交具体的task,在这些无父阶段的stage提交完之后,依赖该stage 的stage才会提交。
8.有向无环图:
答:DAG,有向无环图,简单的来说,就是一个由顶点和有方向性的边构成的图中,从任意一个顶点出发,没有任意一条路径会将其带回到出发点的顶点位置,为每个spark job计算具有依赖关系的多个stage任务阶段,通常根据shuffle来划分stage,如reduceByKey,groupByKey等涉及到shuffle的transformation就会产生新的stage ,然后将每个stage划分为具体的一组任务,以TaskSets的形式提交给底层的任务调度模块来执行,其中不同stage之前的RDD为宽依赖关系,TaskScheduler任务调度模块负责具体启动任务,监控和汇报任务运行情况。
9.Wordcount示例
Spark的运行架构由Driver(可理解为master)和Executor(可理解为worker或slave)组成,Driver负责把用户代码进行DAG切分,划分为不同的Stage,然后把每个Stage对应的task调度提交到Executor进行计算,这样Executor就并行执行同一个Stage的task。
10.RDD是什么以及它的分类:
算子的定义:RDD中定义的函数,可以对RDD中的数据进行转换和操作。下面根据算子类型的分类进行总结:
1. value型算子
从输入到输出可分为一对一(包括cache)、多对一、多对多、输出分区为输入分区自激
1)一对一,
map,简单的一对一映射,集合不变;
flatMap,一对一映射,并将最后映射结果整合;
mappartitions,对分区内元素进行迭代操作,例如过滤等,然后分区不变
glom,将分区内容转换成数据
2)多对一,
union,相同数据类型RDD进行合并,并不去重
cartesian,对RDD内的所有元素进行笛卡尔积操作
3)多对多,
groupBy,将元素通过函数生成相应的Key,然后转化为Key-value格式
4)输出分区为出入分区子集,
filter,对RDD进行过滤操作,结果分区不调整
distinct,对RDD进行去重操作,
subtract,RDD间进行减操作,去除相同数据元素
sample/takeSample 对RDD进行采样操作
5)cache,
cache,将RDD数据原样存入内存
persist,对RDD数据进行缓存操作
2. Key-Value算子
Key-Value算子大致可分为一对一,聚集,连接三类操作
1)一对一,
mapValues,针对数值对中的Value进行上面提到的map操作
2)聚集操作
combineByKey、reduceByKey、partitionBy、cogroup
3)连接
join、leftOutJoin、rightOutJoin
3. Actions算子
该算子通过SparkContext执行提交作业操作,出发RDD DAG的执行
1)foreach, 对RDD中每个元素进行操作,但是不返回RDD或者Array,只返回Unit
2)存入HDFS,saveAsTextFile,saveAsObjectFile
3)scala数据格式,collect,collectAsMap,reduceByKeyLocally, lookup, count, top, reduce, fold, aggregate