Spark面试题整理(更新中)

**1.Spark master使用zookeeper进行HA的,有哪些元数据保存在Zookeeper?
**答:spark通过这个参数spark.deploy.zookeeper.dir指定master元数据在zookeeper中保存的位置,包括Worker,Driver和Application以及Executors。standby节点要从zk中,获得元数据信息,恢复集群运行状态,才能对外继续提供服务,作业提交资源申请等,在恢复前是不能接受请求的。另外,Master切换需要注意2点:
1)在Master切换的过程中,所有的已经在运行的程序皆正常运行!因为SparkApplication在运行前就已经通过Cluster Manager获得了计算资源,所以在运行时Job本身的调度和处理和Master是没有任何关系的!
2) 在Master的切换过程中唯一的影响是不能提交新的Job:一方面不能够提交新的应用程序给集群,因为只有Active Master才能接受新的程序的提交请求;另外一方面,已经运行的程序中也不能够因为Action操作触发新的Job的提交请求;
**2.Spark master HA 主从切换过程不会影响集群已有的作业运行,为什么?
**答:Master切换需要注意2点:
1)在Master切换的过程中,所有的已经在运行的程序皆正常运行!因为SparkApplication在运行前就已经通过Cluster Manager获得了计算资源,所以在运行时Job本身的调度和处理和Master是没有任何关系的!
2) 在Master的切换过程中唯一的影响是不能提交新的Job:一方面不能够提交新的应用程序给集群,因为只有Active Master才能接受新的程序的提交请求;另外一方面,已经运行的程序中也不能够因为Action操作触发新的Job的提交请求;
3.Spark on Mesos中,什么是的粗粒度分配,什么是细粒度分配,各自的优点和缺点是什么?
答:1)粗粒度:启动时就分配好资源, 程序启动,后续具体使用就使用分配好的资源,不需要再分配资源;好处:作业特别多时,资源复用率高,适合粗粒度;不好:容易资源浪费,假如一个job有1000个task,完成了999个,还有一个没完成,那么使用粗粒度,999个资源就会闲置在那里,资源浪费。
2)细粒度分配:用资源的时候分配,用完了就立即回收资源,启动会麻烦一点,启动一次分配一次,会比较麻烦。
4.如何配置spark master的HA?
1)配置zookeeper
2)修改spark_env.sh文件,spark的master参数不在指定,添加如下代码到各个master节点
export SPARK_DAEMON_JAVA_OPTS="- Dspark.deploy.recoveryMode=ZOOKEEPER-Dspark.deploy.zookeeper.url=zk01:2181,zk02:2181,zk03:2181-Dspark.deploy.zookeeper.dir=/spark"
3) 将spark_env.sh分发到各个节点
4)找到一个master节点,执行./start-all.sh,会在这里启动主master,其他的master备节点,启动master命令: ./sbin/start-master.sh
5)提交程序的时候指定master的时候要指定三台master,例如./spark-shell --masterspark://master01:7077,master02:7077,master03:7077
5.Apache Spark有哪些常见的稳定版本,Spark1.6.0的数字分别代表什么意思?
答:常见的大的稳定版本有Spark1.3,Spark1.6, Spark 2.0 ,Spark1.6.0的数字含义
1)第一个数字:1 major version : 代表大版本更新,一般都会有一些 api 的变化,以及大的优化或是一些结构的改变;
2)第二个数字:6 minor version : 代表小版本更新,一般会新加 api,或者是对当前的 api 就行优化,或者是其他内容的更新,比如说 WEB UI 的更新等等;
3)第三个数字:0patch version , 代表修复当前小版本存在的一些 bug,基本不会有任何api 的改变和功能更新;
记得有一个大神曾经说过,如果要切换 spark 版本的话,最好选 patch version 非 0 的版本,因为一般类似于 1.2.0, … 1.6.0 这样的版本是属于大更新的,有可能会有一些隐藏的 bug 或是不稳定性存在,所以最好选择 1.2.1, … 1.6.1 这样的版本。通过版本号的解释说明,可以很容易了解到,spark2.1.1的发布时是针对大版本2.1做的一些bug修改,不会新增功能,也不会新增API,会比2.1.0版本更加稳定。
6.driver的功能是什么?
答: 1)一个Spark作业运行时包括一个Driver进程,也是作业的主进程,具有main函数,并且有SparkContext的实例,是程序的人口点;
2)功能:负责向集群申请资源,向master注册信息,负责了作业的调度,,负责作业的解析、生成Stage并调度Task到Executor上。包括DAGScheduler,TaskScheduler。
7.Spark中Work的主要工作是什么?
答:主要功能:管理当前节点内存,CPU的使用状况,接收master分配过来的资源指令,通过ExecutorRunner启动程序分配任务,worker就类似于包工头,管理分配新进程,做计算的服务,相当于process服务。需要注意的是:
1)worker会不会汇报当前信息给master,worker心跳给master主要只有workid,它不会发送资源信息以心跳的方式给mater,master分配的时候就知道work,只有出现故障的时候才会发送资源。
2)worker不会运行代码,具体运行的是Executor是可以运行具体appliaction写的业务逻辑代码,操作代码的节点,它不会运行程序的代码的。
**8.Spark的有几种部署模式,每种模式特点?
答:1)本地模式Spark不一定非要跑在hadoop集群,可以在本地,起多个线程的方式来指定。将Spark应用以多线程的方式直接运行在本地,一般都是为了方便调试,本地模式分三类· local:只启动一个executor· local[k]:启动k个cpu数目相同的executor
2)standalone模式分布式部署集群, 自带完整的服务,资源管理和任务监控是Spark自己监控,这个模式也是其他模式的基础
3)Spark on yarn模式分布式部署集群,资源和任务监控交给yarn管理,但是目前仅支持粗粒度资源分配方式,包含cluster和client运行模式,cluster适合生产,driver运行在集群子节点,具有容错功能,client适合调试,dirver运行在客户端
4)Spark On Mesos模式。官方推荐这种模式(当然,原因之一是血缘关系)。正是由于Spark开发之初就考虑到支持Mesos,因此,目前而言,Spark运行在Mesos上会比运行在YARN上更加灵活,更加自然。用户可选择两种调度模式之一运行自己的应用程序:1) 粗粒度模式(Coarse-grained Mode):每个应用程序的运行环境由一个Dirver和若干个Executor组成,其中,每个Executor占用若干资源,内部可运行多个Task(对应多少个“slot”)。应用程序的各个任务正式运行之前,需要将运行环境中的资源全部申请好,且运行过程中要一直占用这些资源,即使不用,最后程序运行结束后,回收这些资源。2) 细粒度模式(Fine-grained Mode):鉴于粗粒度模式会造成大量资源浪费,Spark On Mesos还提供了另外一种调度模式:细粒度模式,这种模式类似于现在的云计算,思想是按需分配。
9.Spark技术栈有哪些组件,每个组件都有什么功能,适合什么应用场景?
答:可以分别解释下每个组件的功能和场景
1)Spark core:是其它组件的基础,spark的内核,主要包含:有向循环图、RDD、Lingage、Cache、broadcast等,并封装了底层通讯框架,是Spark的基础。
2)SparkStreaming是一个对实时数据流进行高通量、容错处理的流式处理系统,可以对多种数据源(如Kdfka、Flume、Twitter、Zero和TCP 套接字)进行类似Map、Reduce和Join等复杂操作,将流式计算分解成一系列短小的批处理作业。
3)SparkSQL:Shark是SparkSQL的前身,Spark SQL的一个重要特点是其能够统一处理关系表和RDD,使得开发人员可以轻松地使用SQL命令进行外部查询,同时进行更复杂的数据分析
4)BlinkDB :是一个用于在海量数据上运行交互式 SQL 查询的大规模并行查询引擎,它允许用户通过权衡数据精度来提升查询响应时间,其数据的精度被控制在允许的误差范围内。
5)MLBase是Spark生态圈的一部分专注于机器学习,让机器学习的门槛更低,让一些可能并不了解机器学习的用户也能方便地使用MLbase。MLBase分为四部分:MLlib、MLI、ML Optimizer和MLRuntime。
6)GraphX是Spark中用于图和图并行计算
10.Spark为什么比mapreduce快?

答:1)基于内存计算,减少低效的磁盘交互;
2)高效的调度算法,基于DAG;3)容错机制Linage,精华部分就是DAG和Lingae
两者的具体比较可以看https://blog.csdn.net/qq_43656596/article/details/86300842
11.spark中的RDD是什么,有哪些特性?
RDD(Resilient Distributed Dataset)叫做分布式弹性数据集,是Spark中最基本的数据抽象,它代表一个不可变、可分区、里面的元素可并行计算的集合。
Dataset:就是一个集合,用于存放数据的
Distributed:分布式,可以并行在集群计算
Resilient:表示弹性的:
1)RDD中的数据可以存储在内存或者是磁盘
2)RDD中的分区是可以改变的
12.谈谈spark中的宽窄依赖.
RDD和它依赖的父RDD(s)的关系有两种不同的类型,即窄依赖(narrow dependency)和宽依赖(wide dependency),宽窄依赖是Spark划分stage的根据。
宽依赖:指的是多个子RDD的Partition会依赖同一个父RDD的Partition
窄依赖:指的是每一个父RDD的Partition最多被子RDD的一个Partition使用。
13.spark中如何划分stage?
spark中的stage是根据RDD中的宽窄依赖进行划分的。DAGSchuduler根据lineage绘制DAG有向无环图,然后根据DAG进行划分,从当前job的最后一个算子往前推,遇到宽依赖,那么当前在这个批次中的所有算子操作都划分成一个stage,然后继续按照这种方式在继续往前推,如在遇到宽依赖,又划分成一个stage,一直到最前面的一个算子。最后整个job会被划分成多个stage,而stage之间又存在依赖关系,后面的stage依赖于前面的stage。
14.spark 如何防止内存溢出?
spark的是基于内存运算的,所以最可能出现的问题就是内存溢出,主要有以下几个方面:
1)driver端的内存溢出
在Spark程序中,SparkContext,DAGScheduler都是运行在Driver端的。对应rdd的Stage切分也是在Driver端运行,如果用户自己写的程序有过多的步骤,切分出过多的Stage,这部分信息消耗的是Driver的内存,这个时候就需要调大Driver的内存。
这个时候可以通过增大driver的内存参数:spark.driver.memory (default 1g)来解决。
2)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的过程。
3)数据倾斜导致内存溢出
数据倾斜除了有可能导致内存溢出外,也有可能导致性能的问题,同样可以调用repartition重新分区解决。
4)shuffle后内存溢出
shuffle内存溢出的情况可以说都是shuffle后,单个文件过大导致的。在Spark中,join,reduceByKey这一类型的过程,都会有shuffle的过程,在shuffle的使用,需要传入一个partitioner,大部分Spark中的shuffle操作,默认的partitioner都是HashPatitioner,默认值是父RDD中最大的分区数,这个参数通过spark.default.parallelism控制(在spark-sql中用spark.sql.shuffle.partitions), spark.default.parallelism参数只对HashPartitioner有效,所以如果是别的Partitioner或者自己实现的Partitioner就不能使用spark.default.parallelism这个参数来控制shuffle的并发量了。如果是别的partitioner导致的shuffle内存溢出,就需要从partitioner的代码增加partitions的数量。
5)standalone模式下资源分配不均匀导致内存溢出
在standalone的模式下如果配置了–total-executor-cores 和 –executor-memory 这两个参数,但是没有配置–executor-cores这个参数的话,就有可能导致,每个Executor的memory是一样的,但是cores的数量不同,那么在cores数量多的Executor中,由于能够同时执行多个Task,就容易导致内存溢出的情况。这种情况的解决方法就是同时配置–executor-cores或者spark.executor.cores参数,确保Executor资源分配均匀。
6)使用rdd.persist(StorageLevel.MEMORY_AND_DISK_SER)代替rdd.cache(),也就是改变持久化的方式,减少内存持久化所占的比重。
rdd.cache()和rdd.persist(Storage.MEMORY_ONLY)是等价的,在内存不足的时候rdd.cache()的数据会丢失,再次使用的时候会重算,而rdd.persist(StorageLevel.MEMORY_AND_DISK_SER)在内存不足的时候会存储在磁盘,避免重算,只是消耗点IO时间。
7)减少对象创建,或公用对象等可以减少内存使用
A.使用广播变量broadcast将公用数据共享,可以减少内存使用
B.减少公共对象,这种现象较少,例如:
rdd.flatMap(x=>for(i <- 1 to 1000) yield (“key”,”value”))导致OOM,但是在同样的情况下,使用rdd.flatMap(x=>for(i <- 1 to 1000) yield “key”+”value”)就不会有OOM的问题,这是因为每次(“key”,”value”)都产生一个Tuple对象,而”key”+”value”,不管多少个,都只有一个对象,指向常量池。
15.如何解决spark中的数据倾斜问题?
发现数据倾斜的时候,可以但不应该局限于提高executor的资源来解决问题,通常修改参数或是修改代码就能够解决一般的异常数据。
1、数据问题造成的数据倾斜
首先找出异常的key,一般通过sample算子抽样来判断,
比如: df.select(“key”).sample(false,0.1).(k=>(k,1)).reduceBykey(+).map(k=>(k._2,k._1)).sortByKey(false).take(10)。如果发现个别数据比其他数据大上若干个数量级,则说明发生了数据倾斜。
一般倾斜的数据主要有以下三种情况:
1、null(空值)或是一些无意义的信息()之类的,大多是这个原因引起。
2、无效数据,大量重复的测试数据或是对结果影响不大的有效数据。
3、有效数据,业务导致的正常数据分布。
解决办法
第1,2种情况,直接对数据进行过滤即可(因为该数据对当前业务不会产生影响)。
第3种情况则需要进行一些特殊操作,常见的有以下几种做法
(1) 隔离执行,将异常的key过滤出来单独处理,最后与正常数据的处理结果进行union操作。
(2) 先局部聚合再整体聚合,也就是对key先添加随机值,进行操作后,去掉随机值,再进行一次操作。
(3) 使用reduceByKey 代替 groupByKey(reduceByKey用于对每个key对应的多个value进行merge操作,最重要的是它能够在本地先进行merge操作,并且merge操作可以通过函数自定义.)
(4) 使用map join代替reduce join。在小表不是特别大(取决于你的executor大小)的情况下使用,可以使程序避免shuffle的过程,自然也就没有数据倾斜的困扰了.
2、spark使用不当造成的数据倾斜
可以提高shuffle并行度,如dataFrame和sparkSql可以设置spark.sql.shuffle.partitions参数控制shuffle的并发度,默认为200;rdd操作可以设置spark.default.parallelism控制并发度,默认参数由不同的Cluster Manager控制。
16.SparkStreaming与Kafka的整合方式
1)receiver方式:
在提交 Spark Streaming 任务后,Spark 集群会划出指定的 Receivers 来专门、持续不断、异步读取 Kafka 的数据,读取时间间隔以及每次读取 offsets 范围可以由参数来配置。一般用zookeeper对offset进行保存。当 driver 触发 batch 任务的时候,Receivers 中的数据会转移到剩余的 Executors 中去执行。在执行完之后,Receivers 会相应更新 ZooKeeper 的 offset。如要确保 at least once的读取方式,可以设 spark.streaming.receiver.writeAheadLog.enable 为 true。
2)direct方式:
Direct 方式采用 Kafka 简单的 consumer api 方式来读取数据,无需经由 ZooKeeper,此种方式不再需要专门 Receiver 来持续不断读取数据。当 batch 任务触发时,由 Executor 读取数据,并参与到其他 Executor 的数据计算过程中去。driver 来决定读取多少 offset,并将offset 交由 checkpoint 来维护。Direct 方式无需 Receiver 读取数据,而是需要计算时再读取数据,所以 Direct 方式的数据消费对内存的要求不高,只需要考虑批量计算所需要的内存即可;另外 batch 任务堆积时,也不会影响数据堆积。
两者之间的比较将单独写一篇文章。

猜你喜欢

转载自blog.csdn.net/qq_43656596/article/details/86475555