1、spark中的RDD是什么,有哪些特性
RDD(Resilient Distributed Dataset)叫做弹性分布式数据集,是Spark中最基本的数据抽象,它代表一个不可变、可分区、里面的元素可并行计算的集合。
Dataset:就是一个集合,用于存放数据的
Distributed:分布式,可以并行在集群计算
Resilient:表示弹性的
弹性表示
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 (e.g. to say that the RDD is hash-partitioned)
可选的,针对于kv类型的RDD才具有这个特性,作用是决定了数据的来源以及数据处理后的去向
Optionally, a list of preferred locations to compute each split on (e.g. block locations for an HDFS file)
可选项,数据本地性,数据位置最优
2、概述一下spark中的常用算子区别(map、mapPartitions、foreach、foreachPartition) map:用于遍历RDD,将函数f应用于每一个元素,返回新的RDD(transformation算子)。
foreach:用于遍历RDD,将函数f应用于每一个元素,无返回值(action算子)。
mapPartitions:用于遍历操作RDD中的每一个分区,返回生成一个新的RDD(transformation算子)。
foreachPartition: 用于遍历操作RDD中的每一个分区。无返回值(action算子)。
总结:一般使用mapPartitions或者foreachPartition算子比map和foreach更加高效,推荐使用。
3、谈谈spark中的宽窄依赖
RDD和它依赖的父RDD(s)的关系有两种不同的类型,即窄依赖(narrow dependency)和宽依赖(wide dependency)。
宽依赖:指的是多个子RDD的Partition会依赖同一个父RDD的Partition
指子RDD的分区依赖于父RDD的所有分区,这是因为shuffle类操作,如图中的groupByKey和未经协同划分的join。
窄依赖:指的是每一个父RDD的Partition最多被子RDD的一个Partition使用。
指父RDD的每一个分区最多被一个子RDD的分区所用,表现为一个父RDD的分区对应于一个子RDD的分区,和两个父RDD的分区对应于一个子RDD 的分区。图中,map/filter和union属于第一类,对输入进行协同划分(co-partitioned)的join属于第二类。
4、spark中如何划分stage
rdd当中dag的划分
dag叫做有向无环图,rdd之间一系列的转换,就形成了DAG,dag的主要作用,就是用来划分stage的,
stage叫做一个个的阶段,通过划分stage可以得到taskSet
stage的划分:从最后一个rdd,往前划,遇到窄依赖,加入当前stage,遇到宽依赖,划开一个stage
Stage划分思路
因此spark划分stage的整体思路是:从后往前推,遇到宽依赖就断开,划分为一个stage;遇到窄依赖就将这个RDD加入该stage中。因此在图2中RDD C,RDD D,RDD E,RDDF被构建在一个stage中,RDD A被构建在一个单独的Stage中,而RDD B和RDD G又被构建在同一个stage中。
在spark中,Task的类型分为2种:ShuffleMapTask和ResultTask;简单来说,DAG的最后一个阶段会为每个结果的partition生成一个ResultTask,即每个Stage里面的Task的数量是由该Stage中最后一个RDD的Partition的数量所决定的!而其余所有阶段都会生成ShuffleMapTask;之所以称之为ShuffleMapTask是因为它需要将自己的计算结果通过shuffle到下一个stage中;也就是说图2中的stage1和stage2相当于mapreduce中的Mapper,而ResultTask所代表的stage3就相当于mapreduce中的reducer。
总结:map,filtre为窄依赖, groupbykey为宽依赖 ,遇到一个宽依赖就分一个stage
5、spark-submit的时候如何引入外部jar包
在通过spark-submit提交任务时,可以通过添加配置参数来指定
–driver-class-path 外部jar包
–jars 外部jar包
6、spark 如何防止内存溢出
driver端的内存溢出
可以增大driver的内存参数:spark.driver.memory (default 1g)
这个参数用来设置Driver的内存。在Spark程序中,SparkContext,DAGScheduler都是运行在Driver端的。对应rdd的Stage切分也是在Driver端运行,如果用户自己写的程序有过多的步骤,切分出过多的Stage,这部分信息消耗的是Driver的内存,这个时候就需要调大Driver的内存。
map过程产生大量对象导致内存溢出
这种溢出的原因是在单个map中产生了大量的对象导致的,例如:rdd.map(x=>for(i <- 1 to 10000) yield i.toString),这个操作在rdd中,每个对象都产生了10000个对象,这肯定很容易产生内存溢出的问题。针对这种问题,在不增加内存的情况下,可以通过减少每个Task的大小,以便达到每个Task即使产生大量的对象Executor的内存也能够装得下。具体做法可以在会产生大量对象的map操作之前调用repartition方法,分区成更小的块传入map。例如:rdd.repartition(10000).map(x=>for(i <- 1 to 10000) yield i.toString)。
面对这种问题注意,不能使用rdd.coalesce方法,这个方法只能减少分区,不能增加分区,不会有shuffle的过程。
数据不平衡导致内存溢出
数