Airflow 任务不调度排查:DagRun 语义、并发与依赖定位

按 Airflow 调度语义分层定位任务卡 queued、上游未触发与 DagRun 异常,给出安全重跑与 backfill 顺序

袋鼠云
袋鼠云
生产制造分析

Airflow 任务“不调度”通常不是调度器坏了,而是三种语义被误解:DagRun 只向后创建(历史缺口不自动补)、并发参数让任务排队、依赖或时间表让任务根本没被创建。排查时先分清“任务实例没被创建”“实例存在但卡在 queued”“DagRun 状态异常”三类现象,再按 Airflow 官方 FAQ 与调度语义逐层定位;重跑时先修根因,再 clear,最后才 backfill。

一、四类现象先分型

  • 任务在 UI 中根本没有实例:DagRun 未被创建,指向调度语义或时间问题。
  • 任务实例存在但长期 queued:并发与资源问题。
  • 任务实例存在、上游成功,但下游没触发:依赖与触发规则问题。
  • DagRun 一直 running:内部有卡住的任务,或状态机未推进。

二、根因判定树

第一层:DagRun 为什么没创建

catchup 语义是最高频原因。Airflow 2+ 默认 catchup=False,调度器只创建尚未到期的最新 DagRun,不会补历史缺口;如果调度器因故停机错过窗口,恢复后不会自动创建昨天的实例——这是语义而非故障。需要历史数据时应使用 backfill 或手动创建 DagRun。

其余常见原因:

  • 时间表或时区变更:修改 schedule_interval / timetable 或时区后,data interval 计算变化,已创建实例与新语义不匹配。Airflow 3.x 中调度基于 data interval 与 run_after 语义计算实例,变更后应核对新旧实例边界,避免把“语义变化”误判成“调度器不工作”。
  • DAG 解析失败:import 报错、依赖缺失、语法错误会让整个 DAG 不出现在 UI 或被标记 invalid。用 airflow tasks list <dag_id> 复现解析错误,并查 scheduler 日志的 parsing 报错;一个 DAG 文件内的 import 错误可能连带同目录下的其他 DAG 无法加载,因此报错范围大于单个 DAG 时先修解析问题。
  • 调度器过载:parsing 进程数不足、min_file_process_interval 过大、DAG 数量过多导致解析进程长时间占用,表现为 UI 中 DAG 最后刷新时间很旧。参考 Airflow 最佳实践文档 对 DAG 文件组织与解析开销的建议,优先减少不必要的 import 与模块级计算。
  • 触发条件未满足:dataset 触发的 DAG 在上游数据集未更新前不创建实例;@once 等特殊触发只执行一次,重跑需要显式操作;DAG 被 pause 时实例同样不会创建,排查时应先确认 DAG 的暂停状态。

第二层:任务为什么卡 queued

并发限制有多个层级,任何一个打满都会让任务排队:

  • 全局并发:AIRFLOW__CORE__PARALLELISM,executor 层面的总槽位。
  • DAG 级 DagRun 并发:max_active_runs(默认 1)。常见误配置是 max_active_runs=1 时前一个 DagRun 的任务还在跑,下一时间点的 DagRun 不会启动,表现就是“任务不调度”。
  • DAG 级任务并发:max_active_tasks(旧参数名 concurrency,2.3 起弃用),限制单个 DAG 同时运行的 task instance 数。
  • Pool:任务指定的 pool 及其 slots 被占满;默认池 default_pool 槽位有限。
  • 执行器资源:Celery worker 数量与队列积压,或 Kubernetes executor 的资源配额不足。

判定方法:查看 Admin → Pools 的槽位占用或 airflow pools list;对比 queued 任务所属 pool 与全局 parallelism;检查是否有缺少 execution_timeout 的长时任务占住槽位。可参考 Cloud Composer 关于调度排查的说明 与 Astronomer 对常见 Airflow 错误的总结。

处理要点:提高 parallelism 或 pool slots 之前先确认下游(数据库连接数、外部 API 限流)能否承受;更稳妥的做法是为长任务设置 execution_timeout、拆分大任务、为不同业务建立独立 pool 做隔离。排队与失败的区分也很关键:任务反复失败后再排队,说明问题在任务本身而非并发;任务从未被 executor 领取、日志为空,才优先怀疑并发与执行器资源。

第三层:DagRun 状态异常

DagRun 一直处于 running 的常见原因:内部有任务卡在 queued 或 running,上游未完成导致下游等待;dagrun_timeout 未设置,卡住的运行不会被超时清理;调度器重启后状态机未推进,旧 DagRun 未被正确标记;ExternalTaskSensor 类跨 DAG 依赖一直等待,execution_date_fn 或 poke_timeout 配置不当使 sensor 长期处于 running,进而拖住整个 DagRun。另一类需要单独处理的情况是“任务日志为空的 Heartbeat timed out”:这说明执行器侧与工作节点之间的心跳中断,去 UI 里找任务日志没有意义,应直接排查 executor 与 worker 侧的状态,例如工作节点失联、资源不足或集群回收了执行容器。

离线开发 BatchWorks

支持多模态、覆盖集成开发调度运维的一站式离线数据开发平台

了解产品

三、处理步骤

  1. 先修根因:并发参数、pool、worker 资源、DAG 解析错误、依赖配置。不清除任务实例就重跑,只会重复排队。
  2. 对“未创建的历史实例”:先明确业务上是否需要该时间窗的数据。不需要则忽略;需要则走 backfill。
  3. 对“卡住的任务实例”:从失败点 clear 该任务及其下游,让调度器重新解析依赖。注意 clear 会删除任务实例状态,重跑从 clean 状态开始;只重跑单一任务时优先在 UI 中按任务范围操作,避免命令行时间窗写错把正常实例一并清除。
  4. 对“DagRun 状态异常”:确认内部任务全部处于终态后再处理 DagRun 状态,必要时通过 UI 或 CLI 将 DagRun 标记为 failed,让调度器重新推进;不要在 DagRun 内仍有 running 任务时强行改状态,否则会造成调度器与状态存储不一致。

四、重跑与 backfill 的安全操作顺序

  1. 修复根因并验证(资源、并发、依赖)。
  2. 只重跑受影响任务:从失败点 clear 下游,不要 clear 整个 DAG。
  3. 重跑时间窗与正常调度窗口错开,避免追赶流量与新调度叠加打满并发。
  4. 使用 backfill 时控制并行度(限制 max_active_runs、使用专用 pool),避免打满全局 parallelism 影响生产 DAG。
  5. backfill 前确认下游依赖语义:跨 DAG 依赖需要上游在该时间窗也有实例。
  6. 回放完成后做数据核对(记录数、分区完整性、下游汇总指标),再解除临时限流。

命令参考(参数以实际安装版本的 --help 为准):

# 查看 DAG 列表与运行记录
airflow dags list
airflow dags list-runs -d <dag_id>

# 查看任务与状态
airflow tasks list <dag_id>
airflow tasks state <dag_id> <task_id> --execution-date <YYYY-MM-DD>

# 清除任务实例并重跑(注意时间窗与任务范围)
airflow tasks clear <dag_id> '<task_id>' --start-date <date> --end-date <date>

# 历史回填(先小范围试跑)
airflow dags backfill <dag_id> --start-date <date> --end-date <date>

生产制造

围绕生产调度、系统平衡、关键参数、技术经济指标与单位成本,支撑流程制造生产管理闭环。

了解解决方案

五、常见失败模式

  • 把 catchup=False 下的“历史没跑”当成调度故障,反复重启调度器,实际语义本来就不会补历史。
  • max_active_runs=1 叠加长任务:下一个 DagRun 永远等待,表现为任务不调度。
  • 为绕过排队直接调大 parallelism,把下游数据库连接打爆,引发更大故障。
  • clear 时误勾选 upstream 把无关上游一并清除,造成重复计算或状态错乱。
  • backfill 与正常调度同窗口运行,并发打满,拖慢生产业务。
  • sensor 依赖配置错误导致永久等待未被发现,整条链路每天都在等一个不存在的上游。
  • 把“任务实例未创建”当成“任务执行失败”处理:前者要查调度语义与时间表,后者才查代码与依赖,两者操作对象完全不同。

六、检查清单与监控建议

排查清单:

  • catchup 与 schedule_interval / timetable 当前值是什么,历史缺口是语义还是故障。
  • max_active_runs、max_active_tasks、parallelism、pool 占用四个数值。
  • 是否存在缺少 execution_timeout 的长时任务。
  • DAG 文件是否有解析错误,scheduler 日志的 parsing 负载如何。
  • sensor 的 poke_timeout、reschedule 模式与跨 DAG 依赖是否存在。
  • 卡住的 DagRun 内部是否还有 queued / running 任务。

监控建议:对“queued 超过阈值”“DagRun 超过 dagrun_timeout 仍在 running”“pool 使用率过高”三类指标设告警,通常比任务失败告警更早暴露调度问题;每周抽查一次 catchup 为 False 的关键 DAG 的实例覆盖情况,可以把“该跑没跑”从故障排查变成例行巡检;对并发参数与时间表的每次变更做记录,多数“突然不调度”的实例都能回溯到某一次配置修改。

开启Data+AI 之旅

填写表单,获取专属解决方案

400-002-1024

工作日 9:00-18:00

关注公众号

获取最新产品动态与技术干货

关注公众号

企业微信

扫码添加企业微信