spark 面试题
1、spark 任务如何解决第三方依赖
比如机器学习的包,需要在本地安装?
--py-files
添加
py、zip、egg
文件不需要在各个节点安装
2、spark 数据倾斜怎么解决
spark
中数据倾斜指的是
shuffle
过程中出现的数据倾斜,主要是由于
key
对应的数据不同导致不同
task
所处理的数据量不同。
例如,
reduce
点一共要处理100万条数据,第一个和第二个
task
分别被分配到了1万条数据,计算5分钟内完成,第三个
task
分配到了98万数据,此时第三个
task
可能需要10个小时完成,这使得整个
Spark
作业需要10个小时才能运行完成,这就是数据倾斜所带来的后果。
数据倾斜的表现:
Spark
作业的大部分task
都执行迅速,只有有限的几个task执行的非常慢,此时可能出现了数据倾斜,作业可以运行,但是运行得非常慢Spark
作业的大部分task
都执行迅速,但是有的task在运行过程中会突然报出OOM
,反复执行几次都在某一个task
报出OOM
错误,此时可能出现了数据倾斜,作业无法正常运行
定位数据倾斜问题:
- 查阅代码中的
shuffle
算子,例如reduceByKey、countByKey、groupByKey、join
等算子,根据代码逻辑判断此处是否会出现数据倾斜 - 查看
Spark
作业的log
文件,log
文件对于错误的记录会精确到代码的某一行,可以根据异常定位到的代码位置来明确错误发生在第几个stage
,对应的shuffle
算子是哪一个
3、spark Driver和Executor
在执行
Spark
的应用程序时,
Spark
集群会启动
Driver
和
Executor
两种
JVM
进程,
Driver
:负责创建spark
上下文,提交spark
作业job
,并将作业转换为计算任务task
,在各个Executor
进程间协调任务的调度Excutor
:负责在工作节点执行具体的计算任务,并将结果返回给Driver
,同时为需要持久化的RDD
提供存储功能
4、spark 堆内和堆外内存
spark
内存管理中,涉及到的堆内内存(
On-heap Memory
)和堆外内存(
Off-heap Memory
) 两种,因为
Driver
的内存管理相对简单,因此下面说的内存特指的
Excutor
端的内存
Excutor
作为一个
JVM
进程,其内存管理建立在
JVM
内存管理之上,
Spark
对
JVM
的堆内空间进行更为详细的分配,以便充分利用内存。同时也引入了堆外内存,使之可以直接在工作节点的系统内存中开辟空间,进一步优化了内存的使用。
**堆内内存的大小,由
Spark
应用程序启动时的
–executor-memory
或
spark.executor.memory
参数配置**
堆内内存分区
Excutor
堆内内存主要可分为四大块:
Excutor
内存:主要用于shuffle、join、sort、aggregation
等计算过程中的临时数据Storage
内存:主要用于存储cache
数据,如:rdd
的缓存、unroll
数据- 用户内存
User Memory
:主要用于存储rdd
转换操作需要的数据,如:rdd
依赖等信息 - 预留内存
Reserved Memory
:系统预留内存,用于存储spark
内部对象,防止 OOM,因为spark
堆内内存大小记录是不准确的,需要留出保险区域(在 Spark 2.2.1 中是写死的,其值等于 300MB,这个值是不能修改的)
# systemMaxMemory 取决于当前 JVM 堆内内存大小,其实就是通过 spark.executor.memory 或 --executor-memory 配置的
可用的存储内存 = systemMaxMemory * spark.storage.memoryFraction * spark.storage.safety Fraction
可用的执行内存 = systemMaxMemory * spark.shuffle.memoryFraction * spark.shuffle.safety Fraction
usableMemory = systemMemory - reservedMemory,这个就是 Spark 可用内存
堆外内存
堆外的空间分配较为简单,只有存储内存和执行内存。
可用的执行内存和存储内存占用的空间大小直接由参数
spark.memory.storageFraction
决定,由于堆外内存占用的空间可以被精确计算,所以无需再设定保险区域
spark.memory.offHeap.enabled true
spark.memory.offHeap.size 10737418240
两者区别
内存类别区域划分管理方式优缺点on-heapExecution Memory、Storage Memory、User Memory、Reserved Memory使用 JVM 管理off-heapExecution Memory、Storage Memory手动管理,不经过JVM可以避免频繁的 GC 但是必须自己编写内存申请和释放的逻辑
参考: Spark内存管理之堆内/堆外内存原理详解
5、spark 血缘关系
父子
rdd
的构建存在依赖关系,通过这种依赖关系可以实现
rdd
的容错,多个连续
rdd
的依赖关系成为血缘关系
每个
rdd
不会保存数据,但会保存血缘关系,若当前
rdd
在计算过程中出现错误,可以根据其保存的血缘关系将数据源重新读取进行计算
参考:Spark 之RDD血缘关系
6、spark 宽窄依赖
窄依赖
若依赖关系在设计时即可确定,不需要考虑父
rdd
分区中的记录,且父
rdd
中的每个分区最多只有一个子分区
- 父
rdd
的每个分区最多被一个子rdd
的分区使用 - 子
rdd
中的分区要么只依赖一个父rdd
中的一个分区(如:map、filter
操作) - 要么就是在设计时就能确定子
rdd
是 父rdd
的一个子集(如:coalesce
) - 窄依赖的转换可以在任何的的一个分区上单独执行, 而不需要其他分区的任何信息
宽依赖
- 父
rdd
的分区被多个子rdd
的分区依赖即为宽依赖 - 宽依赖计算时不能随意在某些记录一运行,而是需要使用特殊的方式(如:按照
key
来获取分区中的所以数据) - 如:在排序
sort
时,数据必须被分区,同样范围的key
必须在同一分区 - 具有宽依赖的
transform
操作包括:sort、reduceByKey、groupByKey、join
和调用reParation
函数的任何操作
7、常见的 transform和action 操作
transform
:-map(func)
: 返回一个新的rdd
,其结果由每一个输入元素经过func
函数处理后组成-mapPartition(func)
:类似于map
,但独立地在rdd
每个分片一运行。假设有 n 个元素,m 个分区,map
的函数将被调用n
次,而mapPartition
被调用m
次,一次处理所有分区-flatMap(func)
:对集合中每个元素进行操作然后再扁平化-filter(func)
:返回一个新的rdd
,rdd
中每个元素会经过func
函数的逻辑进行过滤-reduceByKey(func, [numTask])
:在一个(K,V)
的RDD
上调用,返回一个(K,V)
的RDD
,使用reduce
函数将相同key
的值聚合在一起,reduce
任务的个数可以通过第二个参数设置action
:first、count、collect、saveAsTextFile、take、foraech、countByKey
8、spark 有几种部署方式
- Local:运行在一台机器上,通常用来练手或者测试
- Standalone:基于
Master + Slaves
的资源调度集群,spark
任务提交给Master
运行,是spark
自身的一个调度系统 - Yarn:有
yarn-client、yarn-cluster
两种模式,主要区别在于Driver
程序的运行节点,Spark
客户端直接连接Yarn
,不需要额外构建Spark
集群 - Mesos:国内大环境比较少用
版权归原作者 AVICCI 所有, 如有侵权,请联系我们删除。