一. Spark有几种部署方式?请分别简要论述
spark主要有如下四种部署方式,分别如下:
1. Local
Spark运行在一台机器上,通常用于代码测试或者学习。
2. Standalone
构建一个基于Master与Slaves的资源调度集群,Spark任务提交给Master运行。
3. Yarn
Spark客户端直连Yarn,不需要额外构建Spark集群。包含yarn-client和yarn-cluster两种模式,主要区别在于Driver程序的运行节点。
4. Mesos
国内公司用的比较少
二. Spark提交作业参数
在提交任务时的几个重要参数如下:
参数 | 注释 |
---|---|
executor-cores | 每个executor使用的内核数(默认为1) 官方建议2-5个 |
num-executors | 启动executors的数量(默认为2) |
executor-memory | executor内存大小(默认1G) |
driver-cores | driver使用内核数(默认为1) |
driver-memory | driver内存大小(默认512M) |
如下所示为个提交任务的样例
spark-submit \
--master local[8] \
--driver-cores 2 \
--driver-memory 16g \
--executor-cores 4 \
--num-executors 10 \
--executor-memory 8g \
--class PackageName.ClassName XXXX.jar \
--name "Spark Job Name" \
InputPath \
OutputPat
三. 如何理解Spark中RDD的血缘关系?
RDD在血缘依赖方面分为两种,分别是窄依赖(Narrow Dependencies)与宽依赖(Wide Dependencies),它在数据容错以及划分任务时候起到重要作用。
四. 简述Spark的宽窄依赖,以及Spark如何划分stage,每个stage又根据什么决定task个数?
Stage划分:根据RDD之间的依赖关系的不同将Job划分成不同的Stage,当遇到宽依赖时划分新的Stage。
Task个数:每个Stage是一个TaskSet,Stage根据分区数被划分成多个Task。
五. 请列举Spark的transformation算子
1. map(func)
返回一个新的RDD,上游RDD中的每一个输入元素经过func函数转换后返回新的RDD.
2. mapPartitions(func)
与map类似,但其在RDD的每一个分区上独立运行。对于有m个元素,n个分区的RDD,mapPartitions被调用n次(调用次数与分区个数一致)。
3. reduceByKey(func,[numTask])
在一个(K,V) 的RDD上调用,返回一个(K,V)的RDD,reduce函数可以将具有相同key值的元素聚合到一起,reduce任务的并行度可以通过第二个可选的参数来配置。
4. aggregateByKey
(zeroValue:U,[partitioner: Partitioner]) (seqOp: (U, V) => U,combOp: (U, U) => U
在kv对的RDD中,按key将value进行分组合并,合并时将每个value和初始值作为seq函数的参数进行计算,返回的结果作为一个新的kv对,然后再将结果按照key进行合并,最后将每个分组的value传递给combine函数进行计算(先将前两个value进行计算,将返回结果和下一个value传给combine函数,以此类推),将key与计算结果作为一个新的kv对输出。
六. Spark常用算子reduceByKey与groupByKey的区别,哪一种更具优势?
1. reduceByKey
按照key进行聚合,在shuffle之前有预聚合(combine)操作,返回结果为RDD[k,v]。
2. groupByKey
按照key进行分组后直接shuffle(无预聚合)。
预聚合可以提高执行性能,在不影响业务逻辑的情况下,建议优先使用reduceByKey。
七. 请描述Repartition和Coalesce联系与区别
两者都可以用于改变RDD的partition数量,repartition底层调用的就是coalesce方法:
coalesce(numPartitions, shuffle = true)
repartition一定会发生shuffle,coalesce根据传入的参数来判断是否发生shuffle。
一般情况下增大RDD的分区数量使用repartition算子,减少分区数量时使用coalesce算子。
八. 分别简述Spark中的缓存机制与checkpoint机制,并指出两者的区别与联系。
Spark中的缓存机制包括cache与persist,与checkpoint机制一样都是用于RDD持久化。
其中缓存机制不会截断血缘关系。
checkpoint 会截断血缘关系,在checkpoint之前必须没有任何任务提交才会生效,checkpoint过程会额外提交一次任务。
九. 简述Spark中共享变量(广播变量和累加器)的基本原理与用途。
累加器(accumulator)是Spark中提供的一种分布式的变量机制,其原理类似于mapreduce,即分布式的改变,然后聚合这些改变。累加器的一个常见用途是在调试时对作业执行过程中的事件进行计数。而广播变量用来高效分发较大的对象。
共享变量出现的原因:
通常在向 Spark 传递函数时,比如使用 map() 函数或者用 filter() 传条件时,可以使用驱动器程序中定义的变量,但是集群中运行的每个任务都会得到这些变量的一份新的副本,更新这些副本的值也不会影响驱动器中的对应变量。
Spark的两个共享变量,累加器与广播变量分别为结果聚合与广播这两种常见的通信模式突破了这一限制。
十. 当Spark涉及到外部数据库的操作时,如何减少Spark运行中的数据库连接数?
使用foreachPartition代替foreach,
在foreachPartition内获取数据库的连接。
十一. SparkSQL中join操作与left join操作的区别?
SparkSQL中的join算子用于返回的是前面一个集合和后面一个集合可以成功匹配的数据(无法匹配的数据被过滤)。
left join返回结果以第一个RDD为主,第二个RDD中关联不上的记录置为空。
多插一句,部分场景下可以使用left semi join替代left join。
由于 left semi join 是 in (keySet) 的关系,遇到右表重复记录,左表会跳过,性能更高,而 left join 则会一直遍历。需要注意的是left semi join 中最后 select 的结果中只会出现左表中的列。
十二. 请手写出wordcount的Spark代码实现(Scala)
val conf: SparkConf = new SparkConf().setMaster("local[*]").setAppName("WordCount")
val sc = new SparkContext(conf)
sc.textFile("/input")
.flatMap(_.split(" "))
.map((_,1))
.reduceByKey(_+_)
.saveAsTextFile("/output")
sc.stop()
十三. 如何使用Spark实现topN的获取(描述思路或使用伪代码)
方法1:
(1)按照key对数据进行聚合(groupByKey)
(2)将value转换为数组,利用scala的sortBy或者sortWith进行排序(mapValues),需要注意如果数据量太大,会发生OOM。
方法2:
(1)取出所有的key
(2)对key进行迭代,每次取出一个key利用spark的排序算子进行排序
方法3:
(1)自定义分区器,按照key进行分区,使不同的key进到不同的分区
(2)对每个分区运用spark的排序算子进行排序
十四. spark和mapreduce的区别
Spark和MapReduce都是可以处理海量数据,但是在处理方式和处理速度上存在着差异,总结如下:
(1).spark处理数据是基于内存的,而MapReduce是基于磁盘处理数据的。
MapReduce是将中间结果保存到磁盘中,减少了内存占用,牺牲了计算性能。
Spark是将计算的中间结果保存到内存中,可以反复利用,提高了处理数据的性能。
(2).Spark在处理数据时构建了DAG有向无环图,减少了shuffle和数据落地磁盘的次数
Spark 计算比 MapReduce 快的根本原因在于 DAG 计算模型。一般而言,DAG 相比MapReduce 在大多数情况下可以减少 shuffle 次数。Spark 的 DAGScheduler 相当于一个改进版的 MapReduce,如果计算不涉及与其他节点进行数据交换,Spark 可以在内存中一次性完成这些操作,也就是中间结果无须落盘,减少了磁盘 IO 的操作。但是,如果计算过程中涉及数据交换,Spark 也是会把 shuffle 的数据写磁盘的!有一个误区,Spark 是基于内存的计算,所以快,这不是主要原因,要对数据做计算,必然得加载到内存,Hadoop 也是如此,只不过 Spark 支持将需要反复用到的数据给 Cache 到内存中,减少数据加载耗时,所以 Spark 跑机器学习算法比较在行(需要对数据进行反复迭代)
(3).Spark是粗粒度资源申请,而MapReduce是细粒度资源申请
粗粒度申请资源指的是在提交资源时,spark会提前向资源管理器(yarn,mess)将资源申请完毕,如果申请不到资源就等待,如果申请到就运行task任务,而不需要task再去申请资源。
MapReduce是细粒度申请资源,提交任务,task自己申请资源自己运行程序,自己释放资源,虽然资源能够充分利用,但是这样任务运行的很慢。