订阅更新

100个ETL任务谁先跑谁后跑——DAG调度的基本原理

任务调度手册 第 1 题 约 7 分钟读完

本文目录
  1. 先说结论
  2. DAG的核心概念
  3. 调度的三种触发方式
  4. Airflow中的DAG定义
  5. 调度器的核心组件
  6. 实战经验:从Crontab到Airflow的进化
  7. 面试高频追问
  8. 总结

先说结论

DAG(有向无环图)是调度系统的核心数据结构。节点是任务(Task),边是依赖关系(上游完成后下游才能执行)。调度器用拓扑排序确定执行顺序——没有上游依赖的任务先执行,上游都完成后下游才触发。'有向'表示依赖有方向(A完成后才跑B),'无环'表示不能有循环依赖(A依赖B、B又依赖A是不允许的)。

先把这段讲顺;面试官追问时,再展开下面的原理和细节。

详细讲解

数仓里有几百个ETL任务,它们之间有复杂的依赖关系——表A依赖表B和表C,表D依赖表A和表E……怎么确定执行顺序?这就是DAG调度解决的核心问题。在某短视频平台用Airflow管理100+个ETL任务时,最初没有梳理清楚依赖关系,经常出现'下游任务先于上游执行、读到了昨天的旧数据'的问题。后来花了一周理清所有依赖关系构建DAG,问题彻底解决。面试官问调度原理,考的就是你对DAG和任务编排的理解。

DAG的核心概念

# 一个典型的数仓DAG
#
#  数据源A ──→ ODS_A ──→ DWD_订单 ──→ DWS_日报 ──→ ADS_报表
#                           ↑
#  数据源B ──→ ODS_B ──→ DWD_用户 ─┘
#
# 节点(Task):ODS_A, ODS_B, DWD_订单, DWD_用户, DWS_日报, ADS_报表
# 边(Dependency):ODS_A完成后才跑DWD_订单,DWD_订单和DWD_用户都完成后才跑DWS
#
# 拓扑排序的执行顺序:
# 第1轮:ODS_A, ODS_B(没有上游依赖,可以并行)
# 第2轮:DWD_订单, DWD_用户(上游ODS完成后触发,可以并行)
# 第3轮:DWS_日报(DWD都完成后触发)
# 第4轮:ADS_报表(DWS完成后触发)
#
# 关键路径:ODS_A → DWD_订单 → DWS_日报 → ADS_报表
# 关键路径决定了整个DAG的最短完成时间

调度的三种触发方式

触发方式原理适用场景示例
定时触发(Cron)按时间表定期触发日/时/周级批处理每天凌晨1点跑日报ETL
依赖触发上游任务完成后自动触发下游有依赖关系的任务链ODS完成后触发DWD
事件触发外部事件(文件到达/API调用)触发数据到达即处理S3文件上传后触发ETL

Airflow中的DAG定义

# Airflow用Python代码定义DAG和依赖关系
from airflow import DAG
from airflow.operators.python import PythonOperator
from datetime import datetime
 
with DAG('daily_dw_etl',
         schedule='0 1 * * *',  # 每天凌晨1点
         start_date=datetime(2024, 1, 1),
         catchup=False) as dag:
 
    ods_orders = PythonOperator(
        task_id='ods_orders',
        python_callable=extract_orders)
 
    ods_users = PythonOperator(
        task_id='ods_users',
        python_callable=extract_users)
 
    dwd_orders = PythonOperator(
        task_id='dwd_orders',
        python_callable=transform_orders)
 
    dws_daily = PythonOperator(
        task_id='dws_daily',
        python_callable=aggregate_daily)
 
    # 定义依赖关系
    ods_orders >> dwd_orders  # ODS完成后跑DWD
    ods_users >> dwd_orders   # 用户表也要完成
    dwd_orders >> dws_daily   # DWD完成后跑DWS
    # 等价于:dwd_orders.set_upstream([ods_orders, ods_users])

调度器的核心组件

组件职责Airflow实现DolphinScheduler实现
调度器(Scheduler)扫描DAG、判断触发条件、分发任务Airflow Scheduler进程MasterServer
执行器(Executor)实际执行任务LocalExecutor/CeleryExecutor/K8sExecutorWorkerServer
元数据库存储DAG定义、任务状态、运行记录PostgreSQL/MySQLPostgreSQL/MySQL
Web UI可视化DAG、监控任务状态、手动操作Airflow WebserverApiServer+前端

实战经验:从Crontab到Airflow的进化

在某元宇宙社交公司刚搭建数据平台时,没有调度系统——ETL任务直接用Linux Crontab定时执行。

Crontab的问题:① 没有依赖管理——所有任务都是按时间独立触发,DWD可能在ODS还没跑完时就开始了;② 没有失败重试——任务失败了Crontab不知道,不会重试;③ 没有可视化——不知道哪个任务在跑、哪个失败了、依赖链是什么样的;④ 没有告警——凌晨任务失败了早上才发现。

迁移到Airflow后:① 所有依赖关系在DAG中显式定义,下游一定在上游完成后才执行;② 内置重试机制(retries=3),网络抖动等临时问题自动恢复;③ Web UI可以看DAG图、任务状态、运行日志;④ 集成飞书/Slack告警,失败5分钟内通知到人。迁移花了2周,但后续运维效率提升了10倍。

面试高频追问

追问一:为什么DAG不能有环?

如果A依赖B、B依赖A,就形成了环——A等B完成、B等A完成,谁都无法执行,死锁了。拓扑排序的前提就是图中没有环。Airflow在解析DAG时会检测环——发现环直接报错不让DAG运行。

追问二:调度延迟怎么排查?

三个方向:① 调度器本身的延迟——Scheduler扫描DAG的间隔太长或负载太高(Airflow默认每30秒扫描一次);② 执行器排队——任务提交后等Worker空闲(并行度不够或Worker挂了);③ 任务本身慢——SQL没优化或数据量突增。用Airflow的Gantt图看每个任务的等待时间和执行时间可以快速定位。

追问三:一个DAG应该包含多少个Task?

经验值:一个DAG 10-50个Task比较合适。太少(1-2个)失去了任务编排的意义;太多(100+)DAG解析慢、Web UI卡、依赖关系看不清。超过50个Task建议拆成多个DAG用ExternalTaskSensor做跨DAG依赖(第12篇详讲)。

总结

DAG调度面试讲清楚三层:① DAG的核心概念(节点=任务、边=依赖、拓扑排序=执行顺序、关键路径=最短完成时间);② 三种触发方式(定时Cron、依赖触发、事件触发);③ 调度器的核心组件(Scheduler调度+Executor执行+元数据库存储+Web UI监控)。能讲出从Crontab迁移到Airflow的经历和效率提升,面试官会觉得你有真实的调度系统建设经验。

下一篇聊调度器选型:Airflow vs DolphinScheduler vs DataWorks怎么选?

你们用什么调度系统?满意吗?评论区聊聊~

关注「Wood聊大数据」| 9年大数据老兵,聊技术、聊职场、聊成长

刷题进度保存在当前浏览器