Spark面试题(二)

一、spark streaming和storm有何区别?

一个实时毫秒,一个准实时亚秒,不过storm的吞吐率比较低。

二、spark有哪些组件?

Master:管理集群和节点,不参与计算。

Worker:计算节点,进程本身不参与计算,和master汇报。

Driver:运行程序的main方法,创建sparkcontext对象。

Spark context:控制整个application的生命周期,包括DAGSchedular和TaskSchedular等组件。

Client:用户提交程序的入口。

三、spark的工作机制

用户在client端提交作业后,会由Driver运行main方法并创建spark context。

执行RDD算子,形成DAG图输入DAGSchedular,按照RDD之间的依赖关系划分stage输入TaskSchedular。

TaskSchedular会将stage划分为task set分发到各个节点的executor中执行。

四、spark中的宽窄依赖

RDD和他依赖的父RDD的关系有两种不同的类型,即窄依赖(narrow dependency)和宽依赖(wide dependency)。

宽依赖:指的是多个子RDD的partition会依赖同一个父RDD的partition。

窄依赖:指的是每一个父RDD的partition最多被子RDD的一个Partition使用。

父RDD中,每个分区内的数据,都只会被子RDD中特定的分区所消费,为窄依赖:

父RDD中,分区内的数据,会被子RDD内多个分区消费,则为宽依赖:

五、spark中如何划分stage?

1.spark application中可以因为不同的action触发众多的job,一个Application中可以有很多job,每个job是有一个或多个stage构成的,后面的stage依赖于前面的stage,也就是说只有前面的stage计算完毕后,后面的stage才会运行。

2.stage划分的依据是宽依赖,何时产生宽依赖,例如ReduceBykey,GroupByKey的算子,会导致宽依赖的产生。

3.由Action算子(例如collect)导致了SparkContext.RunJob的执行,最终导致了DAGSchedular的submitJob的执行,其核心是通过发送一个case class Jobsubmitted对象给eventProcessLoop。

EventProcessLoop是DAGSchedularEventProcessLoop的具体事例,而DAGSchedularEventProcessLoop是eventLoop的子类,具体实现EventLoop的onReceiver方法,onReceiver方法转过来回调doOnReceive。

4.在handleJobSubmitted中首先创建finalStage,创建finalStage时候会建立父Stage的依赖链条。

总结:依赖是从代码的逻辑层面上来展开说的,可以简单点说:写介绍什么是RDD中的宽窄依赖,然后再根据DAG有向无环图进行划分,从当前job的最后一个算子往前推,遇到宽依赖,那么当前在这个批次中的所有算子操作都划分成一个stage,然后继续按照这种方式再继续往前推,如再遇到宽依赖,又划分成一个stage,一直到最前面的一个算子。最后整个job会被划分成多个stage,而stage之间又存在依赖关系,后面的stage依赖于前面的stage。

六、spark-submit的时候如何引入外部jar包

在通过spark-submit提交任务时,可以通过添加配置参数来指定:

--driver-class-path 外部jar包

--jars 外部jar包

七、spark中cache和persist的区别?

Cache:缓存数据,默认是缓存在内存中,,其本质还是调用persist

Persist:缓存数据,有丰富的缓存策略。数据可以保存在内存也可以保存在磁盘中,使用的时候指定对应的缓存级别。

八、flume整合Spark Streaming问题。

(1)如何实现Spark Streaming读取flume中的数据

可以这样说:

前期经过技术调研,在查看官网资料,发现Spark Streaming整合flume有两种方式:拉模式,推模式。

拉模式:Flume把数据push到Spark Streaming

推模式:Spark Streaming从flume中poll数据

(2)在实际开发的时候是如何保证数据不丢失的

可以这样说:

flume那边采用的channel是将数据落地到磁盘中,保证数据源端安全性(可以在补充一下,flume在这里的channel可以设置为memory内存中,提高数据接收处理的效率,但是由于数据在内存中,安全机制保证不了,故选择channel为磁盘存储。整个流程运行有一点的延迟性)

Spark Streaming通过拉模式整合的时候,使用了FlumeUtils这样一个类,该类是需要依赖一个额外的jar包(spark-streaming-flume_2.10)

要想保证数据不丢失,数据的准确性,可以在构建StreamingConext的时候,利用StreamingContext.getOrCreate(checkpoint, creatingFunc: () => StreamingContext)来创建一个StreamingContext,使用StreamingContext.getOrCreate来创建StreamingContext对象,传入的第一个参数是checkpoint的存放目录,第二参数是生成StreamingContext对象的用户自定义函数。如果checkpoint的存放目录存在,则从这个目录中生成StreamingContext对象;如果不存在,才会调用第二个函数来生成新的StreamingContext对象。在creatingFunc函数中,除了生成一个新的StreamingContext操作,还需要完成各种操作,然后调用ssc.checkpoint(checkpointDirectory)来初始化checkpoint功能,最后再返回StreamingContext对象。

这样,在StreamingContext.getOrCreate之后,就可以直接调用start()函数来启动(或者是从中断点继续运行)流式应用了。如果有其他在启动或继续运行都要做的工作,可以在start()调用前执行。

流失计算中使用checkpoint的作用:

保存元数据,包括流式应用的配置、流式没崩溃之前定义的各种操作、未完成所有操作的batch。元数据被存储到容忍失败的存储系统上,如HDFS。这种ckeckpoint主要针对driver失败后的修复。

保存流式数据,也是存储到容忍失败的存储系统上,如HDFS。这种ckeckpoint主要针对window operation、有状态的操作。无论是driver失败了,还是worker失败了,这种checkpoint都够快速恢复,而不需要将很长的历史数据都重新计算一遍(以便得到当前的状态)。

设置流式数据checkpoint的周期

对于一个需要做checkpoint的DStream结构,可以通过调用DStream.checkpoint(checkpointInterval)来设置ckeckpoint的周期,经验上一般将这个checkpoint周期设置成batch周期的5至10倍。

使用write ahead logs功能

这是一个可选功能,建议加上。这个功能将使得输入数据写入之前配置的checkpoint目录。这样有状态的数据可以从上一个checkpoint开始计算。开启的方法是把spark.streaming.receiver.writeAheadLogs.enable这个property设置为true。另外,由于输入RDD的默认StorageLevel是MEMORY_AND_DISK_2,即数据会在两台worker上做replication。实际上,Spark Streaming模式下,任何从网络输入数据的Receiver(如kafka、flume、socket)都会在两台机器上做数据备份。如果开启了write ahead logs的功能,建议把StorageLevel改成MEMORY_AND_DISK_SER。修改的方法是,在创建RDD时由参数传入。

使用以上的checkpoint机制,确实可以保证数据0丢失。但是一个前提条件是,数据发送端必须要有缓存功能,这样才能保证在spark应用重启期间,数据发送端不会因为spark streaming服务不可用而把数据丢弃。而flume具备这种特性,同样kafka也具备。

九、hadoop和spark的shuffle相同和差异?

(1)从high-level的角度来看,两者并没有大的差别。都是将mapper(Spark中是ShuffleMapTask)的输出进行partition,不同的partition送到不同的reducer(Spark里的reducer可能是下一个stage的ShuffleMapTask,也可能是ResultTask)。Reducer以内存做缓冲区,边shuffle边aggregate数据,等数据aggregate好之后再进行reduce()(Spark里可能是后续的一系列操作)

(2)从low-level的角度来看,两者差距不小。Hadoop MapReduce是sort-based,进入combiner()和reduce()的records必须先sort。这样的好处在于combiner()/reduce()可以处理大规模的数据,因为其输入数据可以通过外排得到(mapper对每段数据先做排序,reducer的shuffle对排好序的每段数据做归并)。目前spark选择的是hash-based,通常使用HashMap对shuffle来的数据进行aggregate,不会对数据进行提前排序。如果用户需要进行排序的数据,那么要自己调用类似SortByKey()的操作。

(3)从现实角度来看,两者也有不小差距。Hadoop MapReduce将处理流程划分出明显的几个阶段:map(),spill,merge,shuffle,sort,reduce()等。每个阶段各司机制,可以按照过程式的编程思想来逐一实现每个阶段的功能。在Spark中,没有这样功能明确的阶段,只有不同的stage和一系列的transformation(),所以spill、sort、aggregate等操作需要蕴含在transformation()中。如果我们将map()端划分数据、持久化数据的过程称为shuffle write,而将reducer读入数据、aggregate数据的过程称为shuffle read。那么在spark中,问题就变成怎么在job的逻辑或者物理执行图中加入shuffle write、shuffle read的处理逻辑,以及两个处理逻辑怎么高效实现。Shuffle write由于不要求数据有序,shuffle write的任务很简单:将数据partition好,并持久化。之所以要持久化,一方面是要减少内存存储空间压力,另一方面也是为了fault-tolerance。

十、RDD的五大特性

(1) A list of partition

一个RDD有一系列的分区/分片

A function for computing each split/partition

对RDD的每一个分区/分片都作用同一个函数

A list of dependencies on others RDDs

有一些依赖,在其他的RDD上

Optionally,a Partitioner for key-value RDDs(e.g to say that the RDD is hash-partitioned)

可选的,对于key-value的RDD的分区策略。

Optionally,a list of preferred locations to compute each split on(e.g. block locations for an HDFS file)

可选的,数据在哪儿优先把作业调度到数据所在节点进行计算:移动数据不如移动计算

十一、spark的优势和劣势

优势:

1.速度快

2.其次,Spark是一个灵活的运算框架,适合做批次处理、工作流、交互式分析、流量处理等不同类型的应用,因此spark也可以成为一个用途广泛的运算引擎,并在未来取代MapReduce的地位

3.最后,Spark可以与Hadoop生态系统的很多组件互相操作。Spark可以运行在新一代资源管理框架YARN上,它还可以读取已有并存放在Hadoop上的数据,这是个非常大的优势

劣势:

1.稳定性方面

2.不能处理大数据

3.不能支持复杂的SQL统计

十二、spark的shuffle过程

1.Spark的shuffle总体而言就包括两个基本的过程:Shuffle write和Shuffle read。ShuffleMapTask的整个执行过程就是Shuffle write。将数据根据hash的结果,将各个Reduce分区的数据写到各自的磁盘中,写数据时不做排序操作。

2.首先是将map的输出结果送到对应的缓冲区bucket中,每个bucket里的文件都会被写入本地磁盘文件ShuffleBlockFile中,形成一个FileSegment文件。

3.Shuffle Read指的是reducer对属于自己的FileSegment文件进行fetch操作,这里采用的netty框架,fetch操作会等到所有的Shuffle write过程结束后再进行,.reducer通过fetch得到的FileSegment先放在缓冲区softBuffer中,默认大小45MB。

十三、spark sql为什么比hive快?

1.消除了冗余的HDFS读写

2.消除了冗余的MapReduce阶段

3.JVM的优化

十四、Spark工作的一个流程

1.构造Spark Application的运行环境(启动SparkContext),SparkContext向资源管理器(可以是standalone、Mesos或Yarn)注册并申请运行Executor资源;

2.资源管理器分配Executor资源,Executor运行情况将随着心跳发送到资源管理器上;

3.SparkContext构建DAG图,将DAG图分解成Stage,并将Taskset发送给TaskSchedular。Executor向SparkContext申请Task,TaskSchedular将Task发送给Executor运行同时SparkContext将应用程序代码发送给Executor。

4.Task在Executor上运行,运行完毕释放所有资源。

十五、对Spark streaming进行性能优化?

1.降低批次处理时间:

①数据接收并行度。

(1)增加DStream:接收网络数据(如Kafka,flume,Socket等)时会对数据进行反序列化再存储在Spark,由于一个DStream只有Receiver对象,如果成为瓶颈可考虑增加DStream。

(2)设置”spark.streaming.blockInterval”参数:接受的数据被存储在Spark内存前,会被合并成block,而block数量决定了task数量;举例,当批次时间间隔为2秒且block时间间隔为200毫秒时,Task数量约为10;如果Task数量过低,则浪费了cpu资源;推荐的最小block时间间隔为50ms。

(3)显式对Input DStream重新分区:再进行更深层次处理前,先对输入数据进行重新分区。

②数据处理并行度:reduceByKey,reduceByKeyAndWindow等operation可通过设置”spark.default.parallelism”参数或显式设置并行度方法参数控制。

③数据序列化:可配置更高效的kryo序列化。

2.设置合理批次时间间隔:

①原则:处理数据的速度应大于或等于数据输入的速度,即批次处理时间大于或等于批次时间间隔。

②方法:

(1)先设置批次时间间隔为5~10秒数据输入速度;

(2)再通过查看log4j日志中的”Total delay”,逐步调整批次时间间隔,保证”Total delay”小于批次时间间隔。

3.内存调优:

①持久化级别:开启压缩,设置参数”spark.rdd.compress”;

②GC策略:在Driver和Executor上开启CMS(Content Management System 内容管理系统)

十六、Spark on Yarn VS standalone

Yarn:你只需要一个节点,然后提交作业即可。这个是不需要spark集群的(不需要启动master和worker)

Standalone:你的spark集群上每个节点上都要部署spark,然后需要启动spark集群。

十七、Spark on Yarn的两种模式

Spark on Yarn支持client和cluster模式:driver运行在哪里

Client:driver运行在本地,提交作业的进程是不能停止的,否则作业就挂了。

Cluster:提交完作业,那么提交作业端就可以断开了,因为driver运行在am(application master)端。

十八、Spark和Hadoop重要概念区分

十九、spark优化之内存管理。

Spark中的内存管理主要分为两个方面:执行和存储。

执行端的内存主要是涉及到shuffle,join,sorts和aggregatations时的计算,存储端的内存主要涉及到cache。在spark中,执行和存储都是共享一个统一的region。当执行端没有使用内存时,存储端就能获得所有的内存信息,反之一样。在必要的时候,执行可以剔除存储,但是存储的时候可以设置一个阈值。

还可以看一个RDD消耗多少内存,在webUI或者使用SizeEstimator’s estimate方法。

内存使用的百分比是(堆内存-300MB)*0.6,执行和存储各占50%

二十、spark优化之广播变量。

使用广播变量在sparkContext中,可以大幅降低每一个序列化task这个对象的大小,集群中启动一个job的成本也会降低。如果你的task中使用了一个大对象(large object),考虑把他优化成一个广播变量。通常来说,一个task大于20KB就值得优化。

Spark两种共享变量:广播变量(broadcast variable)与累加器accumulator

累加器用来对信息进行聚合,而广播变量用来高效分发较大的对象。

共享变量出现的原因:

通常在向 Spark 传递函数时,比如使用 map() 函数或者用 filter() 传条件时,可以使用驱动器程序中定义的变量,但是集群中运行的每个任务都会得到这些变量的一份新的副本,更新这些副本的值也不会影响驱动器中的对应变量。Spark 的两个共享变量,累加器与广播变量,分别为结果聚合与广播这两种常见的通信模式突破了这一限制。

使用广播变量的过程很简单:

(1) 通过对一个类型 T 的对象调用 SparkContext.broadcast 创建出一个 Broadcast[T] 对象。任何可序列化的类型都可以这么实现。

(2) 通过 value 属性访问该对象的值(在 Java 中为 value() 方法)。

(3) 变量只会被发到各个节点一次,应作为只读值处理(修改这个值不会影响到别的节点)

累加器的用法如下所示:

案例如下:

object BroadcastTest {

def main(args: Array[String]): Unit = {

val conf = new SparkConf().setMaster("local").setAppName("broadcast")

val sc = new SparkContext(conf)

val list = List("hello java")

val broadcast = sc.broadcast(list)

val linesRDD = sc.textFile("./word")

linesRDD.filter(line => {

broadcast.value.contains(line)

}).foreach(println)

sc.stop()

}

}

注意事项:

能不能将一个RDD使用广播变量广播出去?

不能,因为RDD是不存储数据的。可以将RDD的结果广播出去。

广播变量只能在Driver端定义,不能在Executor端定义。

(1)通过在driver中调用 SparkContext.accumulator(initialValue) 方法,创建出存有初始值的累加器。返回值为 org.apache.spark.Accumulator[T] 对象,其中 T 是初始值initialValue 的类型。

(2)Spark闭包(函数序列化)里的excutor代码可以使用累加器的 += 方法(在Java中是 add )增加累加器的值。

(3)driver程序可以调用累加器的 value 属性(在 Java 中使用 value() 或 setValue() )来访问累加器的值。

案例如下:

object AccumulatorTest {

def main(args: Array[String]): Unit = {

val conf = new SparkConf().setMaster("local").setAppName("accumulator")

val sc = new SparkContext(conf)

val accumulator = sc.accumulator(0); //创建accumulator并初始化为0

val linesRDD = sc.textFile("./word")

val result = linesRDD.map(s => {

accumulator.add(1) //有一条数据就增加1

s

})

result.collect();

println("words lines is :" + accumulator.value)

sc.stop()

}

}

累加器在Driver端定义赋初始值,累加器只能在Driver端读取,在Excutor端更新

二十一、spark优化之数据本地性。

数据本地性是有很大的影响在Spark job的程序中。如果数据和代码在一起,计算速度就会非常快。但是如果数据和代码是分开的,一个必须要移动到另外一个上去。通常情况下是把序列化后的代码移动到数据所在的节点上,因为代码的大小比数据小很多(移动计算,而不是移动数据)。Spark构建的调度就是基于数据本地性。

数据本地性指的是数据和代码有多近(close)。由近及远有下面locality level:

1.PROCESS_LOCAL:数据在一个相同的正在运行的代码的JVM中。

2.NODE_LOCAL:数据在同一个节点。

3.NO_PREF:数据不管在哪里都可以快速的访问到。(无本地性)

4.RACK_LOCAL:数据在相同的机架上。但是数据在同一个机架的不同server上,需要通过网络传输。

5.ANY:数据在网络的其他地方,不在一个机架上。

Spark会优先安排作业在最佳的locality level上,但是不太可能。

二十二、Spark on Yarn 模式有哪些优点?

1)与其他计算框架共享集群资源(eg.Spark框架与MapReduce框架同时运行,如果不用Yarn进行资源分配,MapReduce分到的内存资源会很少,效率低下);资源按需分配,进而提高集群资源利用等。

2)相较于Spark自带的Standalone模式,Yarn的资源分配更加细致

3)Application部署简化,例如Spark,Storm等多种框架的应用由客户端提交后,由Yarn负责资源的管理和调度,利用Container作为资源隔离的单位,以它为单位去使用内存,cpu等。

4)Yarn通过队列的方式,管理同时运行在Yarn集群中的多个服务,可根据不同类型的应用程序负载情况,调整对应的资源使用量,实现资源弹性管理。

二十三、spark中task有几种类型?

2种类型:1)result task类型,最后一个task,

	  2)是shuffleMapTask类型,除了最后一个task都是。

二十四、spark中map和mapPartition的区别?

rdd的mapPartitions是map的一个变种,它们都可进行分区的并行处理。

两者的主要区别是调用的粒度不一样:map的输入变换函数是应用于RDD中每个元素,而mapPartitions的输入函数是应用于每个分区。

假设一个rdd有10个元素,分成3个分区。如果使用map方法,map中的输入函数会被调用10次;而使用mapPartitions方法的话,其输入函数会只会被调用3次,每个分区调用1次。

这两个方法的另一个区别是在大数据集情况下的资源初始化开销和批处理处理,如果在map和mapPartition中都要初始化一个耗时的资源,然后使用,比如数据库连接。在上面的例子中,mapPartition只需初始化3个资源(3个分区每个1次),而map要初始化10次(10个元素每个1次),显然在大数据集情况下(数据集中元素个数远大于分区数),mapPartitons的开销要小很多,也便于进行批处理操作。

mapPartitionsWithIndex和mapPartitons类似,只是其参数多了个分区索引号。

二十四、python开发spark如何在提交作业的时候添加python的第三方模块?

可以使用--py--files参数,但是应放在运行脚本的前面。所有的import操作必须在context完成之后。

二十五、什么是Spark Executor?

当SparkContext连接到集群管理器时,它会在集群中的节点上获取Executor。 executor是Spark进程,它运行计算并将数据存储在工作节点上。 SparkContext的最终任务被转移到executors以执行它们。

posted @ 2019-02-24 21:52  sandywei  阅读(1092)  评论(0编辑  收藏  举报