工作流基础:概念、DAG、节点与边

大家好,欢迎来到第2章。

上节课我们聊了元数据是什么。今天咱们进入正题——工作流。说白了,工作流就是「任务的编排」。你有一堆事情要做,先做哪个、后做哪个,哪些能并行、哪些必须等,这就是工作流要解决的问题。

工作流是个啥?

我个人习惯把工作流比作「做饭流程」。你去厨房做一顿饭:

  • 先洗菜、切菜(预处理)
  • 再煮饭、炒菜(并行操作)
  • 最后装盘、上桌(输出)

这里就有明确的先后顺序和依赖关系。你不能米还没下锅,就去炒菜。元数据工作流编排也是这个道理——你有一堆数据清洗、转换、验证的任务,得按规矩来。

工作流的核心特征:有依赖、有顺序、有规则。我在项目中遇到过很多次,团队成员把任务一股脑全扔给调度系统,结果跑出来全是错的。为什么?因为依赖没理清楚。

DAG:有向无环图

提到工作流,就绕不开 DAG。这玩意儿中文叫「有向无环图」。名字挺唬人,其实很简单。

有向:任务之间有方向。A → B,表示 A 做完 B 才能开始。方向不能反。

无环:不能出现循环依赖。A 依赖 B,B 依赖 C,C 又依赖 A?那是死锁,跑不起来的。

核心要点:DAG 保证了工作流能「跑完」。任何一个 DAG 都有起点和终点,中间没有死循环。

我曾经接手一个同事留下的工作流,DAG 画成了环。调度器跑了 3 天没停,资源全打满了。查了半天,就是一个依赖配错了。所以,画 DAG 前先想清楚:谁先谁后?有没有循环依赖?

避坑指南:避免在 DAG 中出现隐性循环。比如 A 依赖 B,B 依赖 C,C 又依赖 A 的某个输出。这种情况在复杂项目中很容易发生,特别是多人协作时。我的建议是,每次新增节点,都手动检查一下上游依赖链,别偷懒。

任务节点与边

DAG 由两个基本元素组成:节点(Node)和边(Edge)。

  • 节点:就是一个个具体的任务。比如「清洗数据」「转换格式」「校验元数据」。
  • 边:表示依赖关系。有向边,从上游指向下游。

你想想看,节点是干什么活的,边就是任务之间的「通信线」——告诉调度系统,谁等谁。

节点类型,我遇到过的主要有这几种:

节点类型 说明 举例
源节点 没有上游依赖,工作流的起点 数据接入、文件扫描
处理节点 有入边和出边,做核心计算 数据清洗、特征工程
汇节点 没有下游依赖,工作流的终点 结果存储、通知发送
条件节点 根据输出结果,决定走哪条分支 数据质量校验:通过→继续,不通过→告警

边的语义:边不只是画条线。它隐含了数据传递和状态依赖。比如,A 节点的输出文件,作为 B 节点的输入。B 节点必须等 A 成功后才能启动。如果 A 失败了,B 不会被触发。

经验谈:我刚开始学工作流时,总觉得边就是「画一下」。后来被坑了几次才明白,边的方向决定了一切。配错一条边,轻则算错数据,重则整个流水线瘫痪。所以,画 DAG 时,每条边都要在心里过一遍:「这个方向合理吗?」

一个简单的 DAG 示例

咱们用一个实际的元数据工作流来感受一下。下面这段是 Python 代码片段,用流行的 Airflow 风格表示 DAG:

# 伪代码:样本元数据采集与校验工作流
dag = DAG(
    dag_id='sample_metadata_etl',
    start_date=datetime(2024, 1, 1),
    schedule_interval='@daily'
)

# 定义任务节点
start = DummyOperator(task_id='start', dag=dag)

scan_files = PythonOperator(
    task_id='scan_source_files',
    python_callable=scan_csv_files,
    dag=dag
)

extract_metadata = PythonOperator(
    task_id='extract_metadata',
    python_callable=parse_metadata,
    dag=dag
)

validate_schema = PythonOperator(
    task_id='validate_schema',
    python_callable=check_schema_compliance,
    dag=dag
)

load_to_catalog = PythonOperator(
    task_id='load_to_catalog',
    python_callable=write_to_metastore,
    dag=dag
)

end = DummyOperator(task_id='end', dag=dag)

# 定义依赖关系(边)
start >> scan_files
scan_files >> extract_metadata
extract_metadata >> validate_schema
validate_schema >> load_to_catalog
load_to_catalog >> end

这个例子中,start 是源节点,end 是汇节点。中间四个处理节点串成一条链。每个箭头就是一条边,表示「完成之后才能启动」。实际项目中,你可能会有分支、并行,但底层逻辑是一样的。

DAG 结构图

下面这张图,把上面的例子画成了流程结构。你可以直观看到节点和边的样子:

样本元数据采集与校验工作流 DAG 示例 start scan_files extract_metadata validate_schema load_to_catalog end DAG 核心特性 有向性:所有箭头方向一致,不自相矛盾,不形成环路 无环性:不存在循环依赖,保证工作流一定能在有限步骤内结束

节点与边的实战规则

光知道概念不够。我在实际排产中总结了几条铁律:

  1. 每个节点只干一件事。别把清洗、校验、写入塞到一个节点里。拆开,方便定位问题。
  2. 不要跳过边。有些新手图省事,A 做完直接 C,跳过了 B。除非你真的不需要 B 的结果,否则老老实实连边。
  3. 给边加上数据契约。边不只是依赖,还应该携带数据 schema 约束。A 输出什么格式,B 期望什么格式,写清楚。我见过太多因为字段名对不上而跑崩的工作流。
  4. 画 DAG 前先画草图。我习惯在纸上先画一遍依赖图,确定没有环,再动手写代码。

一句话总结:工作流 = DAG = 节点 + 有向边。节点是任务,边是依赖。画对了,流水线跑得顺;画错了,天天修 bug。

好了,这章基础就讲到这里。记住 DAG 的无环要求,这是工作流能稳定运行的命根子。接下来你可以用这些概念,去实际搭建你的第一个元数据工作流了。