Spark与MR任务卡99%终极排查指南
一、Spark 任务卡在 99%
Spark 是基于内存的 DAG 引擎,任务卡在 99% 通常表现为 某个 Stage 中的个别 Task 永远跑不完,或者 最后一个小 Stage 一直无法提交。
1. 典型现象
- Spark UI 表现:在
Stages页面,某个 Stage 的Tasks进度条中,绝大多数 Task 显示绿色(成功),但有 1~2 个 Task 长期显示黄色(Running)。 - 日志表现:Driver 日志没有报错,但 Executor 日志中可能频繁出现
GC或Task Killed。
2. 核心原因与排查
|
原因 |
排查方法 |
关键指标/日志 |
|---|---|---|
|
数据倾斜 |
查看卡住 Task 的 |
Spark UI -> Stages -> Task 列表 -> |
|
频繁 Full GC |
查看 Executor 的 |
Spark UI -> Executors -> |
|
推测执行干扰 |
查看是否有大量 |
Spark UI -> Stages -> |
|
数据热点 Key |
某个 Key 导致 Join 或 GroupBy 单点处理过慢。 |
检查代码中的 |
|
外部 IO 阻塞 |
读写 HBase、MySQL、Kafka 时网络阻塞或连接池满。 |
Executor 日志中的 |
3. 针对性解决方案
A. 解决数据倾斜(最常见)
- 开启倾斜优化参数:
spark.sql.adaptive.skewJoin.enabled=true // Spark 3.0+ 自适应查询执行 spark.sql.autoBroadcastJoinThreshold=10MB // 强制小表广播 - 代码层面加盐(Salting): 给热点 Key 添加随机前缀,打散数据,聚合后再去掉前缀。
- 使用 Hint 提示:
SELECT /*+ BROADCAST(small_table) */ * FROM large_table JOIN small_table ON ...
B. 优化资源与 GC
- 调整内存比例:
--conf spark.memory.fraction=0.6 # 降低执行内存比例,增加存储内存 --conf spark.memory.storageFraction=0.5 - 增加并行度:
spark.sql.shuffle.partitions=2000 # 默认 200 可能太小,导致单 Task 数据过多
C. 关闭推测执行(如果资源紧张)
推测执行会启动备用 Task,可能加剧资源竞争,导致原 Task 更慢。
spark.speculation=false
二、MapReduce (Hive on MR / Hadoop MR) 任务卡在 99%
MR 是基于磁盘的批处理引擎,任务卡在 99% 通常表现为 Reduce 阶段进度条卡在 99%,因为 个别 Reducer 处理的数据量过大。
1. 典型现象
- YARN/UI 表现:Map 100% 完成,Reduce 进度条长期停留在 99%(例如 99.1%)。
- 任务计数器:查看
Reduce Input Records,发现某个 Reducer 的输入记录数远超平均值。
2. 核心原因与排查
|
原因 |
排查方法 |
关键指标/日志 |
|---|---|---|
|
Reduce 数据倾斜 |
在 MR UI 上点击 |
YARN UI -> Application -> Counters / Reducer Progress |
|
小文件过多 |
Map 任务数量过多,启动开销大,或输出小文件导致下游慢。 |
|
|
Combiner 未生效 |
Shuffle 阶段数据量未减少,导致 Reducer 压力大。 |
检查代码是否设置 Combiner |
|
单节点磁盘故障 |
某个 NodeManager 磁盘 IO 慢,拖慢该节点上的 Reducer。 |
YARN NodeManager 日志 / 磁盘监控 |
|
内存溢出 (OOM) |
Reducer 堆内存不足,频繁 GC 或 Crash 重启。 |
|
3. 针对性解决方案
A. 解决数据倾斜(Hive 参数调优)
Hive 提供了专门的参数来自动处理倾斜:
# 开启数据倾斜优化
set hive.groupby.skewindata=true;
# 调整 Reducer 数量(避免默认 1 个或过多)
set mapreduce.job.reduces=200;
# 或根据输入大小自动计算
set hive.exec.reducers.bytes.per.reducer=256000000;
set hive.exec.reducers.max=1000;
B. 优化 Map/Reduce 内存
# 增加 Map/Reduce 容器内存
set mapreduce.map.memory.mb=4096;
set mapreduce.reduce.memory.mb=8192;
# 增加 Java Heap 比例
set mapreduce.map.java.opts=-Xmx3200m;
set mapreduce.reduce.java.opts=-Xmx6500m;
C. 启用 Combiner
在 Shuffle 之前先在 Map 端进行局部聚合,减少网络传输和 Reducer 压力。
// 代码中设置
job.setCombinerClass(SumCombiner.class);
// 或者 Hive SQL 中通常自动优化,但需检查配置
set hive.map.aggr=true;
D. 处理小文件
# 合并输入小文件
set hive.input.format=org.apache.hadoop.hive.ql.io.CombineHiveInputFormat;
# 合并输出小文件
set hive.merge.mapfiles=true;
set hive.merge.mapredfiles=true;
三、Spark vs MR 核心差异对比表
|
特性 |
Spark 任务卡 99% |
MapReduce 任务卡 99% |
|---|---|---|
|
卡住位置 |
通常是最后一个 Stage 中的个别 Task |
通常是 Reduce 阶段的个别 Reducer |
|
根本原因 |
内存计算导致 GC 频繁,或 Shuffle 分区不均 |
磁盘 IO 瓶颈,或 Key 分布不均导致单 Reducer 过载 |
|
UI 排查 |
Spark UI (4040) -> Stages -> Task Duration |
YARN UI -> Application -> Reducer Progress |
|
倾斜解决 |
|
|
|
资源调整 |
Executor 内存/核数, |
Container 内存 ( |
|
推测执行 |
|
|
|
日志获取 |
Spark UI 直接下载 Executor 日志 |
|
四、快速决策指南
- 先确认引擎:
- 如果是
spark-submit或SparkSQL→ 走 Spark 排查流程。 - 如果是
hive -e(且配置为 MR 引擎) 或hadoop jar→ 走 MR 排查流程。
- 如果是
- 看进度条细节:
- Spark:点进卡住的 Stage,看 Task 列表,是不是有一个 Task 时间特别长?
- MR:点进 Reduce 任务列表,是不是有一个 Reducer 输入数据量特别大?
- 第一刀怎么切:
- Spark:先开
spark.sql.adaptive.enabled=true(Spark 3.x),不行再调shuffle.partitions。 - MR:先开
hive.groupby.skewindata=true,不行再调mapreduce.job.reduces。
- Spark:先开
更多推荐
所有评论(0)