作业跑到一半Task失败了——YARN的容错和推测执行机制
本文目录
先说结论
YARN的容错分三个层次:① Task级别——单个Task失败自动重试(默认4次);② ApplicationMaster级别——AM挂了自动重启(默认2次);③ NodeManager级别——NM挂了上面的Container自动重新分配到其他节点。加上推测执行(Speculative Execution)处理慢Task,构成了完整的容错体系。
先把这段讲顺;面试官追问时,再展开下面的原理和细节。
详细讲解
分布式系统最大的特点就是'一定会有节点出问题'。在某知名出海硬件品牌用EMR时,100+个Spark作业每天跑,几乎每天都有个别Task因为节点故障、网络超时或磁盘错误而失败。但大部分时候作业不需要人工干预就能自动恢复——这就是YARN的容错机制在起作用。面试官问容错,考的是你对分布式系统可靠性设计的理解。
Task级别容错——失败自动重试
# MapReduce的Task重试配置
mapreduce.map.maxattempts = 4 # Map Task最多重试4次
mapreduce.reduce.maxattempts = 4 # Reduce Task最多重试4次
# Spark的Task重试配置
spark.task.maxFailures = 4 # 单个Task最多重试4次
# Task失败重试的流程:
# 1. Task在某个Container上执行失败(OOM/异常/网络超时)
# 2. AM收到失败通知
# 3. AM向RM申请新的Container(可能在不同的NM上)
# 4. 在新Container上重新执行该Task
# 5. 如果4次都失败,该Task标记为永久失败
# 6. 整个Stage/Job因此失败
# 什么情况下Task会失败?
# - Executor OOM(最常见)
# - 数据读取超时(HDFS/S3网络问题)
# - 节点磁盘错误
# - 代码Bug导致异常
ApplicationMaster级别容错——AM挂了自动重启
# AM重试配置
yarn.resourcemanager.am.max-attempts = 2 # AM最多重启2次
# AM重启的场景:
# 1. AM所在的NM节点崩溃
# 2. AM进程OOM(Driver内存不够)
# 3. AM和RM之间心跳超时
# AM重启后会发生什么?
# - MapReduce:AM重启后,已完成的Map Task不需要重跑
# (输出在DataNode的本地磁盘上)
# 只需要重跑正在运行的Task
#
# - Spark:取决于配置
# 开启External Shuffle Service:已完成的Stage不重跑
# 没开启:所有已完成的Stage可能需要重跑
# 建议生产环境开启External Shuffle Service
NodeManager级别容错——节点挂了怎么办?
当一个NM节点挂了(硬件故障、网络断开等),上面运行的所有Container都会丢失:
① RM检测到NM心跳超时(默认10分钟),把该NM标记为Dead。
② 该NM上的所有Container被标记为失败,AM收到通知。
③ AM向RM申请新的Container,在其他存活的NM上重新执行失败的Task。
④ 如果AM本身就在那个NM上,RM会在另一个NM上重启AM,然后AM恢复作业状态。
推测执行(Speculative Execution)——处理慢Task
有时候Task没有失败,但跑得特别慢(比如某个节点磁盘IO高或CPU被其他进程占用)。推测执行会在另一个节点上启动同一个Task的副本,谁先完成就用谁的结果:
# MapReduce推测执行
mapreduce.map.speculative = true # Map端推测执行
mapreduce.reduce.speculative = true # Reduce端推测执行
# Spark推测执行
spark.speculation = true
spark.speculation.interval = 100ms # 检查间隔
spark.speculation.multiplier = 1.5 # 比中位数慢1.5倍触发
spark.speculation.quantile = 0.75 # 75%的Task完成后才检查
# 推测执行的工作原理:
# 1. 等75%的Task完成后开始检查
# 2. 找到比中位数慢1.5倍以上的Task
# 3. 在另一个NM上启动该Task的副本
# 4. 两个副本谁先完成就用谁的结果
# 5. 另一个副本被kill
# 注意:推测执行会消耗额外资源(多启动一个Task)
# 如果集群资源紧张,推测执行可能适得其反
推测执行的适用和不适用场景
| 场景 | 是否开启 | 原因 |
|---|---|---|
| ETL批处理(SLA要求) | 建议开启 | 避免一个慢Task拖慢整个作业 |
| 数据倾斜严重的作业 | 不建议开启 | 慢Task是因为数据多不是节点问题,推测执行也会慢 |
| 有副作用的写操作 | 不建议开启 | 两个副本同时写可能导致数据重复 |
| 集群资源紧张 | 不建议开启 | 推测执行消耗额外资源加剧拥堵 |
| 机器性能差异大 | 建议开启 | 慢节点上的Task可以在快节点上完成 |
实战经验:推测执行反而帮了倒忙
在某知名出海硬件品牌,有一个Spark ETL作业处理设备激活数据,其中有一个Stage的数据倾斜严重——某个测试设备的device_id占了30%的数据量。这个Stage的大部分Task几分钟就完成了,但处理热点数据的那个Task要跑40分钟。
开启推测执行后,Spark认为这个Task'太慢了',在另一个节点上启动了一个副本。但副本处理的也是同样的热点数据,也要跑40分钟。结果两个Task同时运行,白白浪费了一倍的资源,整体执行时间没有任何改善。
解决方案:① 关闭推测执行(spark.speculation = false);② 用AQE(Adaptive Query Execution)处理数据倾斜——AQE自动检测倾斜的Partition并拆分,把40分钟降到了8分钟。这才是治本的方案。
教训:推测执行解决的是'节点性能不均'的问题,不是'数据倾斜'的问题。数据倾斜用AQE或加盐打散解决,推测执行对数据倾斜无效甚至有害。面试时说清楚这个区别是加分点。
YARN的黑名单机制
# 如果某个NM上的Task频繁失败
# YARN会把这个NM加入黑名单
# 后续不再往这个NM上分配Container
# Spark的黑名单配置
spark.blacklist.enabled = true
spark.blacklist.task.maxTaskAttemptsPerNode = 2
# 一个Task在同一个节点上失败2次,该节点被拉黑
spark.blacklist.stage.maxFailedTasksPerExecutor = 2
# 一个Stage在同一个Executor上失败2次,该Executor被拉黑
# 黑名单避免了反复在有问题的节点上重试
# 这是容错机制的重要补充
面试高频追问
追问一:Task重试4次还是失败怎么办?
Task 4次重试都失败后,该Stage标记为失败。Spark的Stage失败会导致整个Job失败(除非开启了Stage重试:spark.stage.maxConsecutiveAttempts)。排查方向:① 看失败日志确定是OOM还是代码Bug;② 如果是OOM调大Executor内存;③ 如果是数据问题(某个文件损坏)跳过或修复数据。
追问二:Spark的External Shuffle Service是什么?
Spark Executor计算的Shuffle数据默认存在Executor本地。如果Executor挂了或被Dynamic Allocation回收,Shuffle数据就丢了,下游Stage要重算。External Shuffle Service是一个独立进程运行在每个NM上,管理Shuffle数据。即使Executor挂了Shuffle数据还在,不需要重算。生产环境强烈建议开启。
追问三:MapReduce和Spark的容错有什么区别?
MapReduce的容错更强——每个Map Task的输出写在本地磁盘上,即使Executor挂了数据还在。Spark的中间数据默认在Executor内存里,Executor挂了就丢了。但Spark可以通过Checkpoint和External Shuffle Service弥补这个差距。总的来说MapReduce的容错简单粗暴但可靠,Spark的容错更精细但配置更复杂。
总结
YARN容错面试讲三个层次:① Task级别(失败重试4次,在其他节点重跑);② AM级别(AM挂了RM重启,恢复作业状态);③ NM级别(NM挂了Container重新分配)。推测执行是处理慢Task的补充手段,但要注意:推测执行解决的是节点性能问题,不是数据倾斜问题——数据倾斜要用AQE。能讲出推测执行帮倒忙的真实案例,面试官会觉得你对容错机制有深度理解。
下一篇聊一个经典对比题:MapReduce为什么比Spark慢10倍?执行模型到底差在哪里?
你们Spark作业开启推测执行了吗?效果怎么样?评论区聊聊~
关注「Wood聊大数据」| 9年大数据老兵,聊技术、聊职场、聊成长