一个任务卡住了后面30个任务全堵了——任务超时和熔断
先说结论
任务超时+SLA监控是防止'不死不活'的两道防线。每个任务都应该设execution_timeout——超时后Airflow自动kill任务并标记失败,触发重试或告警。SLA设在DAG级别——整个DAG没在预期时间内完成就告警。两者结合才能覆盖所有异常场景。
# 任务级超时
from datetime import timedelta
task = PythonOperator(
task_id='dwd_events',
execution_timeout=timedelta(hours=1), # 超1小时自动kill
...)
# 超时后Airflow:
# 1. 发送SIGTERM给任务进程
# 2. 等grace_period秒(默认5秒)
# 3. 发送SIGKILL强制终止
# 4. 任务标记为failed
# 5. 触发重试或on_failure_callback
# 不同任务设不同超时
# ODS加载:30分钟(数据量固定不大)
# DWD清洗:1小时(可能有复杂逻辑)
# DWS聚合:30分钟
# 外部API调用:10分钟
先把这段讲顺;面试官追问时,再展开下面的原理和细节。
详细讲解
在某短视频平台,有一个DWD任务因为数据倾斜跑了4小时(平时30分钟),下游30个任务全部等着。等发现时已经超SLA了。问题不在于任务失败——任务没失败只是很慢,所以没触发失败告警。这种'不死不活'的状态比失败更可怕。解决方案:给每个任务设超时时间,超时就kill+告警。
超时时间怎么设?
| 任务类型 | 平均耗时 | 超时设置 | 倍数 | 原因 |
|---|---|---|---|---|
| ODS加载 | 10分钟 | 30分钟 | 3倍 | 数据量偶尔波动 |
| DWD清洗 | 20分钟 | 60分钟 | 3倍 | 留余量给倾斜 |
| DWS聚合 | 15分钟 | 45分钟 | 3倍 | 标准倍数 |
| API调用 | 30秒 | 5分钟 | 10倍 | 网络不稳定 |
| 数据校验 | 5分钟 | 15分钟 | 3倍 | 标准倍数 |
经验公式:超时 = 平均耗时 × 3倍。太短会误杀正常任务(数据量偶尔增大),太长起不到保护作用。API调用因为网络不确定性可以设更宽松(10倍)。定期Review超时配置——如果某个任务频繁接近超时说明需要优化任务本身。
熔断机制——跳过卡住的非关键任务
# 场景:某个非关键维度表加载卡住了
# 不应该阻塞整条关键路径
# 方案一:Sensor超时后跳过
from airflow.sensors.external_task import ExternalTaskSensor
sensor = ExternalTaskSensor(
task_id='wait_for_dim_table',
timeout=3600, # 等1小时
soft_fail=True, # 超时后标记skipped而不是failed
mode='reschedule',
...)
# soft_fail=True 超时不触发失败告警
# 下游任务根据trigger_rule决定是否继续
# 方案二:下游用ALL_DONE触发规则
downstream = PythonOperator(
task_id='dws_report',
trigger_rule='all_done', # 上游skipped也执行
...)
# 维度表加载被跳过时,DWS用旧维度表数据继续跑
实战经验:DWD数据倾斜导致4小时超时
在某短视频平台,dwd_events任务平时30分钟。某天上游数据中一个热门视频产生了10倍平时的互动数据,数据倾斜导致Spark作业从30分钟变成4小时。下游30个任务全部等待。
事后改进:① 给dwd_events设execution_timeout=90分钟(3倍平均耗时);② 超时后自动重试一次(加了Spark AQE倾斜处理参数后重试通常能在1小时内完成);③ 重试也超时就告警人工介入;④ 对非关键下游任务加soft_fail=True的Sensor,关键路径不受非关键上游影响。
效果:类似的数据倾斜再次发生时:90分钟超时自动kill、重试加AQE参数后50分钟完成、下游在超时kill后10分钟内开始执行。总延迟从4小时降到约2小时,而且关键报表的延迟只有90分钟(非关键维度用旧数据兜底不等待)。
Airflow的Pool——控制并发防止资源争抢
# Pool限制同类任务的最大并发
# 防止太多Spark作业同时跑抢占集群资源
# 在Airflow UI或代码中创建Pool
# Pool 'spark_jobs': slots=10
task = SparkSubmitOperator(
task_id='dwd_events',
pool='spark_jobs', # 分配到spark_jobs池
...)
# 集群最多同时跑10个Spark作业
# 第11个任务排队等Pool空闲
# 防止50个Spark作业同时提交把YARN打满
面试高频追问
追问一:execution_timeout和dagrun_timeout区别?
execution_timeout是单个Task的超时——超时kill这个Task。dagrun_timeout是整个DAG Run的超时——超时后不会kill正在运行的Task但会阻止新Task启动。两者配合使用:Task级超时防止单个任务卡住,DAG级超时防止整个DAG运行太久。
追问二:任务被kill后数据一致性怎么保证?
靠幂等性(第5篇讲过)。如果任务用INSERT OVERWRITE,被kill后重跑不会有数据问题。如果用INSERT INTO就可能有部分数据写入——这也是为什么幂等性如此重要。
追问三:怎么发现任务在变慢(还没超时但趋势不好)?
监控任务的历史执行时间——如果当前耗时超过历史P95(95分位),提前告警。Airflow可以在on_execute_callback中记录开始时间,定期检查是否超过阈值。或者用外部监控(Datadog/Grafana)看任务耗时趋势图,异常突增自动告警。
总结
超时和熔断讲三层:① 任务超时(execution_timeout=平均×3倍,超时自动kill+重试+告警);② 熔断跳过(soft_fail=True让非关键任务超时后skipped而不是failed,不阻塞下游);③ Pool并发控制(限制同类任务最大并发防止资源争抢)。能讲出数据倾斜导致4小时卡死的案例和超时+AQE重试的改进方案,面试官觉得你对调度可用性有深度思考。
下一篇聊数据就绪检测:昨天的数据今天才到怎么办?Sensor模式详解。
你们任务设了超时吗?遇到过卡住的情况吗?评论区聊聊~
关注「Wood聊大数据」| 9年大数据老兵,聊技术、聊职场、聊成长