spark 基础问题面试题
以下是 Spark 基础面试题的全面梳理,涵盖核心概念、架构原理和编程模型,帮助快速掌握高频考点:
一、核心概念
1. Spark 核心组件
组件 | 作用 |
---|---|
Driver | 执行 main() 方法,调度任务,管理集群资源 |
Executor | 在 Worker 节点执行 Task,缓存数据 |
Cluster Manager | 管理集群资源(YARN/Mesos/Standalone) |
RDD | 分布式数据集的抽象(弹性、不可变、可分区的集合) |
2. RDD 五大特性
图表

代码
- 分区(Partitions)、依赖(Dependencies)、计算函数(Compute)、分区器(Partitioner)、首选位置(Preferred Locations)
3. 宽窄依赖
类型 | 特点 | 例子 |
---|---|---|
窄依赖 | 1个父RDD分区 → 1个子RDD分区 | map() , filter() |
宽依赖 | 1个父RDD分区 → N个子RDD分区(需Shuffle) | groupByKey() , reduceByKey() |
二、执行流程
4. Spark 任务提交流程
-
Driver 创建
SparkContext
-
向 Cluster Manager 申请资源
-
Cluster Manager 启动 Executor
-
Driver 将 DAG 拆分为 Task
-
Executor 执行 Task 并返回结果
5. Stage 划分原理
-
触发条件:遇到宽依赖(Shuffle)时切割 Stage
-
Stage 类型:
-
ShuffleMapStage:输出 Shuffle 数据
-
ResultStage :执行 Action 算子(如
count()
,collect()
)
-
6. Shuffle 过程
图表

代码
- 优化点 :减少数据传输量(
reduceByKey
>groupByKey
)
三、编程模型
7. Transformation vs Action
类型 | 特点 | 例子 |
---|---|---|
Transformation | 惰性执行,生成新RDD | map() , filter() , join() |
Action | 触发Job执行,返回结果 | count() , saveAsTextFile() , collect() |
8. 持久化方法对比
方法 | 存储级别 | 是否保留血缘 |
---|---|---|
cache() |
MEMORY_ONLY |
保留 |
persist() |
可指定(如 MEMORY_AND_DISK ) |
保留 |
checkpoint() |
可靠存储(HDFS) | 切断血缘 |
9. 广播变量 vs 累加器
特性 | 广播变量 | 累加器 |
---|---|---|
用途 | 只读共享大变量 | 分布式计数器 |
修改权限 | Executor 只读 | Executor 累加,Driver 读取 |
场景 | 字典表、配置参数 | 统计异常记录数 |
四、部署与资源
10. 部署模式对比
模式 | 特点 | 适用场景 |
---|---|---|
Local | 单机多线程调试 | 开发测试 |
Standalone | Spark自带资源调度 | 中小规模集群 |
YARN | 集成Hadoop资源管理 | 生产环境(主流) |
Kubernetes | 容器化部署 | 云原生架构 |
11. 资源参数配置
bash
spark-submit \
--master yarn \
--num-executors 10 \ # Executor数量
--executor-cores 4 \ # 每个Executor的CPU核数
--executor-memory 8g \ # 每个Executor的内存
--driver-memory 2g # Driver内存
五、高频考点
12. RDD vs DataFrame vs Dataset
特性 | RDD | DataFrame | Dataset |
---|---|---|---|
类型安全 | 是 | 否 | 是 |
优化能力 | 无 | Catalyst优化器支持 | 支持 |
序列化 | Java序列化 | Tungsten二进制格式 | Tungsten二进制格式 |
13. Spark SQL 执行流程
-
将 SQL 解析为逻辑计划
-
Catalyst 优化器进行规则优化(谓词下推、列裁剪)
-
生成物理计划并执行
14. 常见算子作用
-
coalesce()
:减少分区数(无Shuffle) -
repartition()
:调整分区数(触发Shuffle) -
sample()
:数据采样 -
union()
:合并两个RDD(不去重)
六、故障排查基础
15. OOM(内存溢出)原因
-
Driver OOM:
collect()
拉取数据过多 -
Executor OOM:
-
数据倾斜导致单Task负载过高
-
persist()
缓存数据超出内存
-
16. Shuffle Fetch Failed 错误
-
原因:Executor 宕机或 GC 超时
-
解决 :增加超时时间
spark.network.timeout=300s
附:基础代码示例
scala
// 创建RDD
val rdd = sc.parallelize(List(1, 2, 3, 4, 5))
// Transformation: 过滤偶数
val evenRDD = rdd.filter(_ % 2 == 0)
// Action: 计数并打印
println(evenRDD.count()) // 输出: 2
// 广播变量使用
val broadcastVar = sc.broadcast(Map("a" -> 1, "b" -> 2))
rdd.map(x => broadcastVar.value.getOrElse(x.toString, 0)).collect()
💡 面试技巧:
被问及概念时,先给出定义 ,再说明应用场景(如:"宽依赖在Shuffle时发生,典型场景是groupByKey")
遇到原理题,结合流程图描述(如DAG划分、Shuffle过程)
牢记核心参数作用(如
num-executors
,executor-cores
)
spark 面试题
以下是针对Spark面试的核心知识点梳理及高频考题解析,结合企业实际需求和性能调优经验整理而成,助你系统准备:
一、核心概念与架构
-
Spark vs MapReduce 效率对比
-
内存计算:中间数据优先内存存储,减少磁盘I/O(MapReduce需落盘)14
-
DAG调度:将任务拆解为有向无环图,优化执行路径;MapReduce仅两阶段(Map+Reduce)38
-
容错机制:RDD血缘(Lineage)自动恢复丢失数据;MapReduce需任务重跑16
-
-
RDD(弹性分布式数据集)
-
五大特性:
-
分区容错(自动分区恢复)
-
血缘追溯(Lineage重建丢失数据)
-
存储弹性(内存不足时自动溢写磁盘)
-
计算弹性(Task/Stage自动重试)
-
分片弹性(动态调整分区数)28
-
-
缺陷:不支持细粒度更新(如单条记录修改),仅适合批处理8
-
-
部署模式
| 模式 | 特点 | 适用场景 |
|----------------|--------------------------------------|------------|----|
| Local | 单机多线程调试,无集群资源管理 | 开发测试 |
| Standalone | Spark自带资源调度,Master单点故障需ZK支持 | 中小集群 |
| YARN | 资源由YARN管理,支持Cluster(生产)和Client(调试)模式 | Hadoop生态集成 |
| Mesos | 细粒度资源分配(按需调度),但配置复杂 | 动态资源场景 | 68 |
二、调度与执行机制
-
Stage划分原理
-
宽窄依赖:
-
窄依赖(Narrow):1父分区 → 1子分区(如
map
、filter
),同Stage内流水线执行 -
宽依赖(Wide):1父分区 → N子分区(如
groupByKey
),需Shuffle并划分新Stage23
-
-
划分算法:从Action算子反向回溯,遇宽依赖则切割Stage4
-
-
Shuffle机制详解
-
过程:
-
Shuffle Write:Map端按Key分区,排序后溢写磁盘文件
-
Shuffle Read:Reduce端拉取数据,聚合后计算38
-
-
优化:
-
避免
groupByKey
→ 改用reduceByKey
(Map端预聚合) -
调整分区数:
spark.sql.shuffle.partitions
(默认200,按数据量调优)78
-
-
-
内存管理
-
统一内存模型(Unified Memory):
-
Execution内存(计算):Shuffle/Join等临时数据
-
Storage内存(存储):缓存RDD数据
-
两者可动态抢占,避免OOM6
-
-
调参 :
spark.memory.fraction
(默认0.6,总JVM内存占比)8
-
三、性能优化实战
-
数据倾斜解决
-
现象:个别Task耗时远高于其他
-
方案:
-
两阶段聚合:加随机前缀局部聚合 → 全局聚合
-
热点Key分离 :单独处理或使用
salting
(添加随机后缀) -
开启倾斜处理 :
spark.sql.adaptive.skewedJoin.enabled=true
(Spark 3.0+)47
-
-
-
算子选择原则
-
优先
reduceByKey
>groupByKey
(减少Shuffle数据量) -
用
mapPartitions
替代map
(减少函数调用开销) -
避免
collect()
全量拉数据 → 改用take(N)
或输出到存储系统7
-
-
持久化策略
| 机制 | 存储位置 | 血缘保留 | 适用场景 |
|-------------------|-----------|----------|------------|----|
| cache/persist | 内存/磁盘 | 保留 | 频繁访问的中间RDD |
| checkpoint | HDFS等可靠存储 | 切断 | 长血缘依赖的容错恢复 | 34 |
四、容错与高级特性
-
Master HA实现
-
ZooKeeper存储:Application、Worker、Driver元数据2
-
主备切换:不影响运行中任务(Driver与Executor直连)24
-
-
广播变量与累加器
-
广播变量:只读缓存大对象(如字典表),避免Task重复传输7
-
累加器:分布式计数器(如统计异常记录数),仅Driver可读1
-
-
Spark Streaming容错
-
Checkpoint:定时保存DStream血缘及元数据至HDFS
-
WAL预写日志:接收数据后先写日志再处理4
-
五、编程实践与SQL优化
-
Spark SQL性能优势
-
Catalyst优化器:逻辑计划优化(谓词下推、列裁剪)
-
Tungsten引擎:堆外内存管理 + 代码生成5
-
-
DataFrame vs SQL
-
DataFrame优势:
-
编译时类型安全
-
链式调用优化更友好
-
支持DSL复杂操作4
-
-
-
典型代码示例
scala
// WordCount实现 val textFile = sc.textFile("hdfs://path/to/file") val counts = textFile.flatMap(line => line.split(" ")) .map(word => (word, 1)) .reduceByKey(_ + _) counts.saveAsTextFile("hdfs://output") // TopN获取(按频次降序) val topN = counts.map(_.swap).sortByKey(false).take(N)
附:高频扩展题
-
解释
coalesce
与repartition
区别?(coalesce
无Shuffle缩分区,repartition
强制Shuffle)7 -
为何Executor会Full GC?(堆内存不足或对象未释放;调大
spark.executor.memoryOverhead
)6 -
Spark on YARN下Container如何复用?(Yarn Container不支持复用,每次任务启动新Container)8
💡 提示 :面试中常结合场景深度追问(如"百亿数据JOIN如何优化?"),建议准备1-2个调优实战案例,重点说明问题定位→解决策略→效果量化(如:"两阶段聚合解决数据倾斜,耗时从2小时降至20分钟")。
spark 性能调优面试题
以下是 Spark 性能调优的高频面试题及深度解析,涵盖原理、实战场景和解决方案,帮助你在面试中展现调优思维:
一、核心调优方向
1. 数据倾斜(Data Skew)
-
问题现象:少数 Task 处理时间远超其他 Task(如 99% Task 1min 完成,1个 Task 卡 1h)。
-
解决方案:
- 加盐打散 :对热点 Key 添加随机前缀(
concat(key, '_', rand()%10)
),先局部聚合再全局聚合。
scala
// 第一阶段:加盐局部聚合 val saltedRDD = rdd.map(key => (s"${key}_${Random.nextInt(10)}", 1)) val partialAgg = saltedRDD.reduceByKey(_ + _) // 第二阶段:去盐全局聚合 val restoredRDD = partialAgg.map{ case (saltedKey, count) => val key = saltedKey.split("_")(0) (key, count) } val finalResult = restoredRDD.reduceByKey(_ + _)
-
分离热点数据 :单独处理热点 Key(
filter
拆分 → 分别计算 →union
)。 -
开启 AQE (Spark 3.0+):
spark.sql.adaptive.skewedJoin.enabled=true
自动拆分倾斜分区。
- 加盐打散 :对热点 Key 添加随机前缀(
2. Shuffle 优化
-
核心问题:Shuffle 写磁盘 + 网络传输是最大瓶颈。
-
调优手段:
-
减少 Shuffle 数据量:
-
避免
groupByKey
→ 改用reduceByKey
(Map 端预聚合)。 -
使用
broadcast join
替代shuffle join
(小表 < 10MB)。
-
-
调整分区数:
-
合理设置
spark.sql.shuffle.partitions
(默认200,建议:集群核数*2~4
)。 -
动态分区:
spark.sql.adaptive.enabled=true
(AQE 自动合并小分区)。
-
-
选择 Shuffle 管理器:
SortShuffleManager
(默认,支持压缩) >HashShuffleManager
(易 OOM)。
-
3. 内存管理
-
堆内存结构:
图表
-
关键参数:
参数 作用 推荐值 spark.executor.memory
Executor 总内存 根据集群调整 spark.memory.fraction
Execution+Storage 占比 0.6~0.8 spark.storage.memoryFraction
Storage 内存占比 0.3~0.5 -
OOM 解决:
- 增加
spark.executor.memoryOverhead
(堆外内存,默认 executor-memory * 0.1)。
- 增加
二、执行效率优化
4. 执行计划优化
-
查看执行计划:
scala
df.explain("extended") // 展示逻辑/物理计划
-
Catalyst 优化器生效点:
-
谓词下推(Predicate Pushdown):提前过滤数据。
-
列裁剪(Column Pruning):仅读取必要列。
-
-
强制广播 :
spark.sql.autoBroadcastJoinThreshold=10485760
(10MB)。
5. 资源并行度
-
黄金公式:
text
总并行度 = Executor 数 * 每个 Executor 的 core 数
-
参数配置:
参数 说明 优化建议 spark.executor.instances
Executor 数量 根据数据量调整 spark.executor.cores
每个 Executor 的 CPU 核数 4~8 spark.default.parallelism
RDD 默认分区数 设为总 core 数 2~3 倍
6. 数据读写优化
-
输入数据:
-
优先使用 Parquet/ORC(列式存储 + 谓词下推)。
-
避免小文件:
spark.sql.files.maxPartitionBytes=128MB
(合并小文件)。
-
-
输出数据:
-
用
coalesce
减少输出文件数(无 Shuffle)。 -
禁用生成
_SUCCESS
文件:spark.hadoop.mapreduce.fileoutputcommitter.marksuccessfuljobs=false
。
-
三、高级调优技巧
7. Join 优化策略
Join 类型 | 适用场景 | 调优手段 |
---|---|---|
Broadcast Join | 小表 + 大表 | 自动触发或手动 hint |
Sort Merge Join | 大表 + 大表 | 确保分区有序 |
Bucket Join | 预分桶表 | 创建分桶表 CLUSTERED BY |
8. 序列化优化
-
Kryo 序列化:
scala
spark.conf.set("spark.serializer", "org.apache.spark.serializer.KryoSerializer") spark.conf.registerKryoClasses(Array(classOf[MyClass]))
-
效果:比 Java 序列化快 10 倍,体积小 50%。
9. AQE(自适应查询执行)
-
Spark 3.0+ 核心特性:
scala
spark.sql("SET spark.sql.adaptive.enabled=true")
-
功能:
-
动态合并小分区(
Coalesce Shuffle Partitions
)。 -
倾斜 Join 自动优化(
Optimize Skewed Join
)。 -
运行时调整 Join 策略(
Demote BroadcastHashJoin
)。
-
四、故障排查实战题
10. 慢 Task 定位
-
步骤:
-
查看 Spark UI 定位慢 Task 所在 Stage。
-
检查该 Task 的 GC 时间(是否频繁 Full GC)。
-
分析输入数据量(是否数据倾斜)。
-
-
工具:
-
spark.eventLog.enabled=true
记录日志。 -
使用 Spark History Server 离线分析。
-
11. Shuffle Fetch Failed
-
错误原因:
-
Executor 宕机导致 Shuffle 数据丢失。
-
GC 停顿超时(默认 60s)。
-
-
解决方案:
-
增加超时:
spark.network.timeout=300s
。 -
减少 GC:切 G1 垃圾回收器。
bash
--conf "spark.executor.extraJavaOptions=-XX:+UseG1GC"
-
调优思维回答模板
面试官:"如果遇到 Spark 作业运行缓慢,你会如何排查?"
回答框架:
定位瓶颈:通过 Spark UI 分析 Stage/Task 耗时,确认是 Shuffle、计算还是 IO 问题。
数据倾斜检查 :观察 Task 数据分布,用
df.stat.approxQuantile
检测 Key 分布。内存分析:检查 GC 日志,调整内存比例或切 G1 回收器。
优化执行 :启用 AQE、广播小表、避免
collect
全量拉取。资源调整:根据数据量动态增加 Executor 或 core 数。
案例佐证:"曾优化某 Join 作业,通过广播表+AQE 倾斜处理,从 2h 降至 20min"。
掌握这些核心点,你将能系统性应对 90% 的 Spark 调优面试问题!