【算法工程师】spark面试问题总结

spark中的RDD是什么,有哪些特性?

RDD(Resilient Distributed Dataset)叫做弹性分布式数据集,是spark中最基本的数据抽象,它代表一个不可变,可分区,里面的元素可以并行计算的集合。

  1. RDD中的数据可以存储在内存或者磁盘中;
  2. RDD中的分区是可以改变的;

【五大特性】

  1. A list of partitions:一个分区列表,RDD中的数据都存储在一个分区列表中
  2. A function for computing each split:作用在每一个分区中的函数=
  3. A list of dependencies on other RDDs:一个RDD依赖于其他多个RDD,这个点很重要,RDD的容错机制就是依据这个特性而来的
  4. Optionally,a Partitioner for key-value RDDs(eg:to say that the RDD is hash-partitioned):可选的,针对于kv类型的RDD才有这个特性,作用是决定了数据的来源以及数据处理后的去向
  5. 可选项,数据本地性,数据位置最优

概述一下spark中的常用算子区别(map,mapPartitions,foreach,foreachPatition)

  1. map:用于遍历RDD,将函数应用于每一个元素,返回新的RDD(transformation算子)
  2. foreach:用于遍历RDD,将函数应用于每一个元素,无返回值(action算子)
  3. mapPatitions:用于遍历操作RDD中的每一个分区,返回生成一个新的RDD(transformation算子)
  4. foreachPatition:用于遍历操作RDD中的每一个分区,无返回值(action算子)
    总结:一般使用mapPatitions和foreachPatition算子比map和foreach更加高效,推荐使用

谈谈spark中的宽窄依赖

RDD和它的父RDD的关系有两种类型:窄依赖和宽依赖

  1. 宽依赖:指的是多个子RDD的Partition会依赖同一个父RDD的Partition,关系是一对多,父RDD的一个分区的数据去到子RDD的不同分区里面,会有shuffle的产生
  2. 窄依赖:指的是每一个父RDD的Partition最多被子RDD的一个partition使用,是一对一的,也就是父RDD的一个分区去到了子RDD的一个分区中,这个过程没有shuffle产生
  3. 区分的标准就是看父RDD的一个分区的数据的流向,要是流向一个partition的话就是窄依赖,否则就是宽依赖。

spark中如何划分stage

Spark任务会根据RDD之间的依赖关系,形成一个DAG有向无环图,DAG会提交给DAGScheduler,DAGScheduler会把DAG划分相互依赖的多个stage,划分依据就是宽窄依赖,遇到宽依赖就划分stage,每个stage包含一个或多个task,然后将这些task以taskSet的形式提交给TaskScheduler运行,stage是由一组并行的task组成

  1. spark程序中可以因为不同的action触发众多的job,一个程序中可以有很多的job,每一个job是由一个或者多个stage构成的,后面的stage依赖于前面的stage,也就是说只有前面依赖的stage计算完毕后,后面的stage才会运行;
  2. stage 的划分标准就是宽依赖:何时产生宽依赖就会产生一个新的stage,例如reduceByKey,groupByKey,join的算子,会导致宽依赖的产生;
  3. 切割规则:从后往前,遇到宽依赖就切割stage;
  4. 计算格式:pipeline管道计算模式,piepeline只是一种计算思想,一种模式
  5. spark的pipeline管道计算模式相当于执行了一个高阶函数,也就是说来一条数据然后计算一条数据,会把所有的逻辑走完,然后落地,而MapReduce是1+1=2,2+1=3这样的计算模式,也就是计算完落地,然后再计算,然后再落地到磁盘或者内存,最后数据是落在计算节点上,按reduce的hash分区落地。管道计算模式完全基于内存计算,所以比MapReduce快的原因。
  6. 管道中的RDD何时落地:shuffle write的时候,对RDD进行持久化的时候。
  7. stage的task的并行度是由stage的最后一个RDD的分区数来决定的,一般来说,一个partition对应一个task,但最后reduce的时候可以手动改变reduce的个数,也就是改变最后一个RDD的分区数,也就改变了并行度。例如:reduceByKey(+,3)
  8. 优化:提高stage的并行度:reduceByKey(+,patition的个数) ,join(+,patition的个数)

DAGScheduler分析

是一个面向stage 的调度器;
主要功能:

  1. 接受用户提交的job;
  2. 将job根据类型划分为不同的stage,记录那些RDD,stage被物化,并在每一个stage内产生一系列的task,并封装成taskset;
  3. 决定每个task的最佳位置,任务在数据所在节点上运行,并结合当前的缓存情况,将taskSet提交给TaskScheduler;
  4. 重新提交shuffle输出丢失的stage给taskScheduler;
    注:一个stage内部的错误不是由shuffle输出丢失造成的,DAGScheduler是不管的,由TaskScheduler负责尝试重新提交task执行。
  5. 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才会提交。
  6. 有向无环图:
    DAG,有向无环图,简单的来说,就是一个由顶点和有方向性的边构成的图中,从任意一个顶点出发,没有任意一条路径会将其带回到出发点的顶点位置,为每个spark job计算具有依赖关系的多个stage任务阶段,通常根据shuffle来划分stage,如reduceByKey,groupByKey等涉及到shuffle的transformation就会产生新的stage ,然后将每个stage划分为具体的一组任务,以TaskSets的形式提交给底层的任务调度模块来执行,其中不同stage之前的RDD为宽依赖关系,TaskScheduler任务调度模块负责具体启动任务,监控和汇报任务运行情况。

RDD缓存

Spark可以使用 persist 和 cache 方法将任意 RDD 缓存到内存、磁盘文件系统中。缓存是容错的,如果一个 RDD 分片丢失,可以通过构建它的 transformation自动重构。被缓存的 RDD 被使用的时,存取速度会被大大加速。一般的executor内存60%做 cache, 剩下的40%做task。
Spark中,RDD类可以使用cache() 和 persist() 方法来缓存。cache()是persist()的特例,将该RDD缓存到内存中。而persist可以指定一个StorageLevel。StorageLevel的列表可以在StorageLevel 伴生单例对象中找到。
在不会使用cached RDD的时候,及时使用unpersist方法来释放它。

RDD共享变量:

在应用开发中,一个函数被传递给Spark操作(例如map和reduce),在一个远程集群上运行,它实际上操作的是这个函数用到的所有变量的独立拷贝。这些变量会被拷贝到每一台机器。通常看来,在任务之间中,读写共享变量显然不够高效。然而,Spark还是为两种常见的使用模式,提供了两种有限的共享变量:广播变量和累加器。

  1. 广播变量(Broadcast Variables)
    1. 广播变量缓存到各个节点的内存中,而不是每个 Task
    2. 广播变量被创建后,能在集群中运行的任何函数调用
    3. 广播变量是只读的,不能在被广播后修改
    4. 对于大数据集的广播, Spark 尝试使用高效的广播算法来降低通信成本
  2. 累加器
    累加器只支持加法操作,可以高效地并行,用于实现计数器和变量求和。Spark 原生支持数值类型和标准可变集合的计数器,但用户可以添加新的类型。只有驱动程序才能获取累加器的值

spark如何防止内存溢出:

  1. 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的过程。

  2. 数据不平衡导致内存溢出
    数据不平衡除了有可能导致内存溢出外,也有可能导致性能的问题,解决方法和上面说的类似,就是调用repartition重新分区。这里就不再累赘了。
    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的数量。
    standalone模式下资源分配不均匀导致内存溢出
    在standalone的模式下如果配置了–total-executor-cores 和 –executor-memory 这两个参数,但是没有配置–executor-cores这个参数的话,就有可能导致,每个Executor的memory是一样的,但是cores的数量不同,那么在cores数量多的Executor中,由于能够同时执行多个Task,就容易导致内存溢出的情况。这种情况的解决方法就是同时配置–executor-cores或者spark.executor.cores参数,确保Executor资源分配均匀。
    使用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时间。

spark中cache和persist的区别

  1. cache:缓存数据,默认是缓存在内存中,其本质还是调用persist
  2. persist:缓存数据,有丰富的数据缓存策略。数据可以保存在内存也可以保存在磁盘中,使用的时候指定对应的缓存级别就可以了。

spark中的数据倾斜的现象,原因,后果,措施

  1. 数据倾斜的现象
    多数task执行速度较快,少数task执行时间非常长,或者等待很长时间后提示你内存不足,执行失败。
  2. 数据倾斜的原因
    1. 数据问题
      1. key本身分布不均衡(包括大量的key为空)
      2. key的设置不合理
    2. spark使用问题
      1. shuffle时的并发度不够
      2. 计算方式有误
  3. 数据倾斜的后果
  4. spark中的stage的执行时间受限于最后那个执行完成的task,因此运行缓慢的任务会拖垮整个程序的运行速度(分布式程序运行的速度是由最慢的那个task决定的)。
  5. 过多的数据在同一个task中运行,将会把executor撑爆。
  6. spark数据倾斜的处理:
    发现数据倾斜的时候,不要急于提高executor的资源,修改参数或是修改程序,首先要检查数据本身,是否存在异常数据。
    1. 数据问题造成的数据倾斜
      找出异常的key
      如果任务长时间卡在最后最后1个(几个)任务,首先要对key进行抽样分析,判断是哪些key造成的。 选取key,对数据进行抽样,统计出现的次数,根据出现次数大小排序取出前几个。
      比如: 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。
注意1: 如果此时依旧存在问题,建议筛选出倾斜的数据单独处理。最后将这份数据与正常的数据进行union即可。
注意2: 单独处理异常数据时,可以配合使用Map Join解决。
2. spark使用不当造成的数据倾斜
提高shuffle并行度
dataFrame和sparkSql可以设置spark.sql.shuffle.partitions参数控制shuffle的并发度,默认为200。
rdd操作可以设置spark.default.parallelism控制并发度,默认参数由不同的Cluster Manager控制。
局限性: 只是让每个task执行更少的不同的key。无法解决个别key特别大的情况造成的倾斜,如果某些key的大小非常大,即使一个task单独执行它,也会受到数据倾斜的困扰。
使用map join 代替reduce join
在小表不是特别大(取决于你的executor大小)的情况下使用,可以使程序避免shuffle的过程,自然也就没有数据倾斜的困扰了.(详细见http://blog.csdn.net/lsshlsw/article/details/50834858、http://blog.csdn.net/lsshlsw/article/details/48694893)
局限性: 因为是先将小数据发送到每个executor上,所以数据量不能太大。

spark中map-side-join关联优化

将多份数据进行关联是数据处理过程中非常普遍的用法,不过在分布式计算系统中,这个问题往往会变的非常麻烦,因为框架提供的 join 操作一般会将所有数据根据 key 发送到所有的 reduce 分区中去,也就是 shuffle 的过程。造成大量的网络以及磁盘IO消耗,运行效率极其低下,这个过程一般被称为 reduce-side-join。

如果其中有张表较小的话,我们则可以自己实现在 map 端实现数据关联,跳过大量数据进行 shuffle 的过程,运行时间得到大量缩短,根据不同数据可能会有几倍到数十倍的性能提升。

何时使用:在海量数据中匹配少量特定数据

原理:reduce-side-join 的缺陷在于会将key相同的数据发送到同一个partition中进行运算,大数据集的传输需要长时间的IO,同时任务并发度收到限制,还可能造成数据倾斜。

将少量的数据转化为Map进行广播,广播会将此 Map 发送到每个节点中,如果不进行广播,每个task执行时都会去获取该Map数据,造成了性能浪费。对大数据进行遍历,使用mapPartition而不是map,因为mapPartition是在每个partition中进行操作,因此可以减少遍历时新建broadCastMap.value对象的空间消耗,同时匹配不到的数据也不会返回。

什么是粗粒度,什么是细粒度,各自的优缺点是什么?

1.粗粒度:启动时就分配好资源,程序启动,后续具体使用就使用分配好的资源,不需要再分配资源。
好处:作业特别多时,资源复用率较高,使用粗粒度。
缺点:容易资源浪费,如果一个job有1000个task,完成了999个,还有一个没完成,那么使用粗粒度。如果有999个资源闲置在那里,会造成资源大量浪费。
2. 细粒度:用资源的时候分配,用完了就立即回收资源,启动会麻烦一点,启动一次分配一次,会比较麻烦。

driver的功能是什么

  1. 一个spark作业运行时包括一个driver进程,也就是作业的主进程,具有main函数,并且有sparkContext的实例,是程序的入口;
  2. 功能:负责向集群申请资源,向master注册信息,负责了作业的调度,负责了作业的解析,生成stage并调度task到executor上,包括DAGScheduler,TaskScheduler。

spark的有几种部署模式,每种模式特点?

  1. 本地模式

Spark不一定非要跑在hadoop集群,可以在本地,起多个线程的方式来指定。将Spark应用以多线程的方式直接运行在本地,一般都是为了方便调试,本地模式分三类
2. standalone模式
分布式部署集群, 自带完整的服务,资源管理和任务监控是Spark自己监控,这个模式也是其他模式的基础,
3. Spark on yarn模式
分布式部署集群,资源和任务监控交给yarn管理,但是目前仅支持粗粒度资源分配方式,包含cluster和client运行模式,cluster适合生产,driver运行在集群子节点,具有容错功能,client适合调试,dirver运行在客户端

  1. Spark On Mesos模式。官方推荐这种模式(当然,原因之一是血缘关系)。正是由于Spark开发之初就考虑到支持Mesos,因此,目前而言,Spark运行在Mesos上会比运行在YARN上更加灵活,更加自然。用户可选择两种调度模式之一运行自己的应用程序:
  1. 粗粒度模式(Coarse-grained Mode):每个应用程序的运行环境由一个Dirver和若干个Executor组成,其中,每个Executor占用若干资源,内部可运行多个Task(对应多少个“slot”)。应用程序的各个任务正式运行之前,需要将运行环境中的资源全部申请好,且运行过程中要一直占用这些资源,即使不用,最后程序运行结束后,回收这些资源。

  2. 细粒度模式(Fine-grained Mode):鉴于粗粒度模式会造成大量资源浪费,Spark On Mesos还提供了另外一种调度模式:细粒度模式,这种模式类似于现在的云计算,思想是按需分配。

Spark技术栈有哪些组件,每个组件都有什么功能,适合什么应用场景?

1)Spark core:是其它组件的基础,spark的内核,主要包含:有向循环图、RDD、Lingage、Cache、broadcast等,并封装了底层通讯框架,是Spark的基础。
2)SparkStreaming是一个对实时数据流进行高通量、容错处理的流式处理系统,可以对多种数据源(如Kdfka、Flume、Twitter、Zero和TCP 套接字)进行类似Map、Reduce和Join等复杂操作,将流式计算分解成一系列短小的批处理作业。
3)Spark sql:Shark是SparkSQL的前身,Spark SQL的一个重要特点是其能够统一处理关系表和RDD,使得开发人员可以轻松地使用SQL命令进行外部查询,同时进行更复杂的数据分析
4)BlinkDB :是一个用于在海量数据上运行交互式 SQL 查询的大规模并行查询引擎,它允许用户通过权衡数据精度来提升查询响应时间,其数据的精度被控制在允许的误差范围内。
5)MLBase是Spark生态圈的一部分专注于机器学习,让机器学习的门槛更低,让一些可能并不了解机器学习的用户也能方便地使用MLbase。MLBase分为四部分:MLlib,MLI、ML Optimizer和MLRuntime。
6)GraphX是Spark中用于图和图并行计算

spark中worker 的主要工作是什么?

主要功能:管理当前节点内存,CPU的使用情况,接受master发送过来的资源指令,通过executorRunner启动程序分配任务,worker就类似于包工头,管理分配新进程,做计算的服务,相当于process服务,需要注意的是:
1.worker会不会汇报当前信息给master?worker心跳给master主要只有workid,不会以心跳的方式发送资源信息给master,这样master就知道worker是否存活,只有故障的时候才会发送资源信息;
2.worker不会运行代码,具体运行的是executor,可以运行具体application斜的业务逻辑代码,操作代码的节点,不会去运行代码。

Mapreduce和Spark的都是并行计算,那么他们有什么相同和区别

  1. 两者都是用mr模型来进行并行计算。
  2. hadoop的一个作业称为job,job里面分为map task和reduce task,每个task都是在自己的进程中运行的,当task结束时,进程也会结束。
  3. spark用户提交的任务成为application,一个application对应一个sparkcontext,app中存在多个job,每触发一次action操作就会产生一个job。这些job可以并行或串行执行,每个job中有多个stage,stage是shuffle过程中DAGSchaduler通过RDD之间的依赖关系划分job而来的,每个stage里面有多个task,组成taskset有TaskSchaduler分发到各个executor中执行,executor的生命周期是和app一样的,即使没有job运行也是存在的,所以task可以快速启动读取内存进行计算。
  4. hadoop的job只有map和reduce操作,表达能力比较欠缺而且在mr过程中会重复的读写hdfs,造成大量的io操作,多个job需要自己管理关系。
  5. spark的迭代计算都是在内存中进行的,API中提供了大量的RDD操作如join,groupby等,而且通过DAG图可以实现良好的容错。

RDD机制?

rdd分布式弹性数据集,简单的理解成一种数据结构,是spark框架上的通用货币。 所有算子都是基于rdd来执行的,不同的场景会有不同的rdd实现类,但是都可以进行互相转换。 rdd执行过程中会形成dag图,然后形成lineage保证容错性等。 从物理的角度来看rdd存储的是block和node之间的映射。

spark有哪些组件?

  1. master:管理集群和节点,不参与计算。
  2. worker:计算节点,进程本身不参与计算,和master汇报。
  3. Driver:运行程序的main方法,创建spark context对象。
  4. spark context:控制整个application的生命周期,包括dagsheduler和task scheduler等组件。
  5. client:用户提交程序的入口。

spark工作机制

用户在client端提交作业后,会由Driver运行main方法并创建spark context上下文。 执行add算子,形成dag图输入dagscheduler,按照add之间的依赖关系划分stage输入task scheduler。 task scheduler会将stage划分为task set分发到各个节点的executor中执行。

cache后面能不能接其他算子,它是不是action操作?

  1. cache可以接其他算子,但是接了算子之后,起不到缓存应有的效果,因为会重新触发cache。
  2. cache不是action操作

reduceByKey是不是action?

不是,很多人都会以为是action,reduce rdd是action

数据本地性是在哪个环节确定的?

具体的task运行在那他机器上,dag划分stage的时候确定的。

RDD的弹性表现在哪几点?

  1. 自动的进行内存和磁盘的存储切换;
  2. 基于Lingage的高效容错;
  3. task如果失败会自动进行特定次数的重试;
  4. stage如果失败会自动进行特定次数的重试,而且只会计算失败的分片;
  5. checkpoint和persist,数据计算之后持久化缓存;
  6. 数据调度弹性,DAG TASK调度和资源无关;
  7. 数据分片的高度弹性。

常规的容错方式有哪几种类型?

  1. 数据检查点,会发生拷贝,浪费资源;
  2. 记录数据的更新,每次更新都会记录下来,比较复杂且比较消耗性能。

RDD通过Linage(记录数据更新)的方式为何很高效?

  1. lazy记录了数据的来源,RDD是不可变的,且是lazy级别的,且rDD之间构成了链条,lazy是弹性的基石。由于RDD不可变,所以每次操作就产生新的rdd,不存在全局修改的问题,控制难度下降,所有有计算链条将复杂计算链条存储下来,计算的时候从后往前回溯900步是上一个stage的结束,要么就checkpoint;
  2. 记录原数据,是每次修改都记录,代价很大,如果修改一个集合,代价就很小,官方说rdd是粗粒度的操作,是为了效率,为了简化,每次都是操作数据集合,写或者修改操作,都是基于集合的rdd的写操作是粗粒度的,rdd的读操作既可以是粗粒度的也可以是细粒度,读可以读其中的一条条的记录;
  3. 简化复杂度,是高效率的一方面,写的粗粒度限制了使用场景如网络爬虫,现实世界中,大多数写是粗粒度的场景。

RDD有哪些缺陷?

  1. 不支持细粒度的写和更新操作(如网络爬虫),spark写数据是粗粒度的(所谓粗粒度,就是批量写入数据,为了提高效率。但是读数据是细粒度的也就是说可以一条条的读);
  2. 不支持增量迭代计算,Flink支持

说一说Spark程序编写的一般步骤?

初始化,资源,数据源,并行化,rdd转化,action算子打印输出结果或者也可以存至相应的数据存储介质。

Spark有哪些聚合类的算子,我们应该尽量避免什么类型的算子?

在我们的开发过程中,能避免则尽可能避免使用reduceByKey、join、distinct、repartition等会进行shuffle的算子,尽量使用map类的非shuffle算子。这样的话,没有shuffle操作或者仅有较少shuffle操作的Spark作业,可以大大减少性能开销。

对于Spark中的数据倾斜问题你有什么好的方案?

前提是定位数据倾斜,是OOM了,还是任务执行缓慢,看日志,看WebUI

  1. 避免不必要的shuffle,如使用广播小表的方式,将reduce-side-join提升为map-side-join;
  2. 分拆发生数据倾斜的记录,分成几个部分进行,然后合并join后的结果;
  3. 改变并行度,可能并行度太少了,导致个别task数据压力大;
  4. 两阶段聚合,先局部聚合,再全局聚合;
  5. 自定义paritioner,分散key的分布,使其更加均匀。

RDD创建有哪几种方式?

  1. 使用程序中的集合创建rdd;
  2. 使用本地文件系统创建rdd;
  3. 使用hdfs创建rdd;
  4. 基于数据库db创建rdd;
  5. 基于Nosql创建rdd,如hbase;
  6. 基于s3创建rdd;
  7. 基于数据流,如socket创建rdd

Spark并行度怎么设置比较合适

答:spark并行度,每个core承载2 ~ 4个partition,如,32个core,那么64 ~ 128之间的并行度,也就是设置64 ~ 128个partion,并行读和数据规模无关,只和内存使用量和cpu使用时间有关。

Spark中数据的位置是被谁管理的?

每个数据分片都对应具体物理位置,数据的位置是被blockManager,无论数据是在磁盘,内存还是tacyan,都是由blockManager管理。

Spark的数据本地性有哪几种?

  1. PROCESS_LOCAL是指读取缓存在本地节点的数据
  2. NODE_LOCAL是指读取本地节点硬盘数据
  3. ANY是指读取非本地节点数据

通常读取数据PROCESS_LOCAL>NODE_LOCAL>ANY,尽量使数据以PROCESS_LOCAL或NODE_LOCAL方式读取。其中PROCESS_LOCAL还和cache有关,如果RDD经常用的话将该RDD cache到内存中,注意,由于cache是lazy的,所以必须通过一个action的触发,才能真正的将该RDD cache到内存中。

rdd有几种操作类型?

  1. transformation,rdd由一种转为另一种rdd
  2. action
  3. cronroller,crontroller是控制算子,cache,persist,对性能和效率的有很好的支持

Spark如何处理不能被序列化的对象?

将不能序列化的内容封装成object

collect功能是什么,其底层是怎么实现的?

driver通过collect把集群中各个节点的内容收集过来汇总成结果,collect返回结果是Array类型的,collect把各个节点上的数据抓过来,抓过来数据是Array型,collect对Array抓过来的结果进行合并,合并后Array中只有一个元素,是tuple类型(KV类型的)的。

为什么Spark Application在没有获得足够的资源,job就开始执行了,可能会导致什么什么问题发生?

会导致执行该job时候集群资源不足,导致执行job结束也没有分配足够的资源,分配了部分Executor,该job就开始执行task,应该是task的调度线程和Executor资源申请是异步的;如果想等待申请完所有的资源再执行job的:需要将spark.scheduler.maxRegisteredResourcesWaitingTime设置的很大;spark.scheduler.minRegisteredResourcesRatio 设置为1,但是应该结合实际考虑否则很容易出现长时间分配不到资源,job一直不能运行的情况。

map与flatMap的区别

  1. map:对RDD每个元素转换,文件中的每一行数据返回一个数组对象
  2. flatMap:对RDD每个元素转换,然后再扁平化将所有的对象合并为一个对象,文件中的所有行数据仅返回一个数组对象,会抛弃值为null的值。

列举你常用的action

collect,reduce,take,count,saveAsTextFile等。

Spark为什么要持久化,一般什么场景下要进行persist操作?

  1. spark所有复杂一点的算法都会有persist身影,spark默认数据放在内存,非常适合高速迭代,1000个步骤中只有第一个输入数据,中间不产生临时数据,但分布式系统风险很高,所以容易出错,就要容错,rdd出错或者分片可以根据血统算出来,如果没有对父rdd进行persist 或者cache的化,就需要重头做。
  2. 以下场景需要进行persist操作:
    1. 某个步骤计算非常耗时,需要进行persist持久化;
    2. 计算链条非常长,重新恢复要算很多步骤;
    3. checkpoint所在的rdd要持久化。persist是lazy级别,框架发现有checnkpoint,checkpoint时单独触发一个job,需要重算一遍,checkpoint前要持久化,写个rdd.cache或者rdd.persist,将结果保存起来,再写checkpoint操作,这样执行起来会非常快,不需要重新计算rdd链条了。checkpoint之前一定会进行persist。
    4. shuffle之后为什么要persist,shuffle要进性网络传输,风险很大,数据丢失重来,恢复代价很大;
    5. shuffle之前进行persist,框架默认将数据持久化到磁盘,这个是框架自动做的。

为什么要进行序列化

优点:序列化可以减少数据的体积,减少存储空间,高效存储和传输数据;
缺点:不好的是使用的时候要反序列化,非常消耗CPU。

介绍一下join操作优化经验

join其实常见的就分为两类:map-side joinreduce-side join
当大表和小表join时,用map-side join能显著提高效率。将多份数据进行关联是数据处理过程中非常普遍的用法,不过在分布式计算系统中,这个问题往往会变的非常麻烦,因为框架提供的 join 操作一般会将所有数据根据 key 发送到所有的 reduce 分区中去,也就是 shuffle 的过程。造成大量的网络以及磁盘IO消耗,运行效率极其低下,这个过程一般被称为 reduce-side-join。如果其中有张表较小的话,我们则可以自己实现在 map 端实现数据关联,跳过大量数据进行 shuffle 的过程,运行时间得到大量缩短,根据不同数据可能会有几倍到数十倍的性能提升。

介绍一下cogroup rdd实现原理,你在什么场景下用过这个rdd?

这个实现根据两个要进行合并的两个RDD操作,生成一个CoGroupedRDD的实例,这个RDD的返回结果是把相同的key中两个RDD分别进行合并操作,最后返回的RDD的value是一个Pair的实例,这个实例包含两个Iterable的值,第一个值表示的是RDD1中相同KEY的值,第二个值表示的是RDD2中相同key的值。由于做cogroup的操作,需要通过partitioner进行重新分区的操作,因此,执行这个流程时,需要执行一次shuffle的操作(如果要进行合并的两个RDD的都已经是shuffle后的rdd,同时他们对应的partitioner相同时,就不需要执行shuffle)。

你可能感兴趣的:(求职)