初识 Prefect:为什么我放弃了 Airflow

先说说我的经历吧。2019 年我刚接手一个数据管道项目,团队用的是 Airflow。每天光 DAG 解析失败就要花半小时排查,调度器动不动就卡死。后来我偶然翻到了 Prefect 的文档,试用了一周就决定全量迁移。嗯,这个决定让我少加了半年班。

Prefect 是什么?说白了,它是一个 Python 原生的工作流编排框架。你写几个函数,加个装饰器,就能搭出一条生产级的数据管道。它不要求你学 XML、YAML 或者什么 DSL,你写的代码就是工作流本身。

一句话总结:Prefect = Python 函数 + 装饰器 + 自动重试 + 可视化 UI。它让工作流编排变得像写普通脚本一样自然。

为什么选择 Prefect?

我选工具的标准很简单:上手快、不出错、好排查。Prefect 在这三点上都做得不错。

  • Python 原生——你不需要学第二套配置语言。写个 @flow 和 @task 就完事了。
  • 自动重试与状态管理——任务失败后自动重试,状态持久化到数据库,挂了也不丢进度。
  • 开箱即用的 UI——不用搭 Grafana、不用写 PromQL,Prefect Server 自带仪表盘,运行历史、日志、任务图全都有。
  • 本地开发友好——你可以在 Jupyter Notebook 里调试 flow,跑通了再部署到生产。

我的习惯:新项目我总会先搭一个 Prefect 本地环境,写三个 task 串起来跑一遍。能跑通,再考虑 Docker 化。这个流程帮我筛掉了不少配置问题。

Prefect vs Airflow:一张表说清楚

很多人问我:有 Airflow 为什么还要用 Prefect?我直接拿项目里的真实对比给你看。

对比维度 Airflow Prefect
定义方式 DAG 文件 + Python 运算符 Python 装饰器(@flow / @task)
调度器 单点,容易成为瓶颈 无中心调度器,任务直接提交
重试机制 需手动配置 retries 默认自动重试,可配置指数退避
状态持久化 依赖数据库轮询 事件驱动,状态实时写入
本地调试 需要启动全套服务 单进程即可运行
学习曲线 中等(需理解运算符、钩子、池等概念) 低(会写函数就会用)

你看,Airflow 更像一个重型框架,适合大团队统一管控。Prefect 则轻巧得多,适合快速迭代的团队。我个人建议:团队小于 10 人、项目迭代快、不想在基础设施上花太多时间,直接上 Prefect。

避坑指南:我曾经在一个项目里强行用 Airflow 做实时流处理,结果调度器每 30 秒轮询一次,数据库连接数直接打满。后来换成 Prefect 的事件驱动模式,连接数降了 80%。选工具一定要看场景,别盲目追大厂方案。

核心概念速览

Prefect 的核心概念其实就四个,你花十分钟就能理解。

  • Flow——工作流的容器。你可以把它理解成一个函数,里面编排了多个任务。
  • Task——工作流里的最小执行单元。每个 task 就是一个 Python 函数,加上 @task 装饰器。
  • State——任务或流程的状态。比如 Pending、Running、Success、Failed。Prefect 会把状态持久化,方便你回溯。
  • Runner——执行引擎。它负责调度 task、处理依赖、管理重试。

下面这张图展示了它们之间的关系:

Prefect 核心概念关系图 Flow(工作流容器) Task A 数据采集 Task B 数据清洗 Task C 数据入库 每个 Task 都有独立 State:Pending → Running → Success / Failed State 持久化到数据库,支持回溯与重试 Runner(执行引擎) 负责调度 Task、解析依赖、触发重试、记录日志

你想想看,一个 Flow 里可以串行、并行、甚至条件分支。每个 Task 独立运行,互不干扰。如果 Task B 失败了,Prefect 会自动重试,或者根据你的配置跳过、暂停整个 Flow。这种灵活性在实际项目中太重要了。

我的建议:刚开始学 Prefect,别急着搭集群。先在本机装个 prefect 包,写一个包含三个 task 的 flow,跑通它。你会在 10 分钟内理解 80% 的核心概念。

一个最小示例

光说不练假把式。下面这个例子,你复制到本地就能跑。

from prefect import flow, task

@task
def fetch_data():
    return [1, 2, 3, 4, 5]

@task
def compute_sum(data):
    return sum(data)

@task
def print_result(result):
    print(f"计算结果: {result}")

@flow
def my_first_flow():
    data = fetch_data()
    total = compute_sum(data)
    print_result(total)

if __name__ == "__main__":
    my_first_flow()

运行后你会看到控制台输出 计算结果: 15。就这么简单。你不需要写 DAG 文件,不需要启动调度器,一个 Python 文件搞定一切。

注意:如果你在 Jupyter Notebook 里运行,记得先执行 prefect config set PREFECT_ORION_DATABASE_CONNECTION_URL="sqlite:///./prefect.db",否则状态不会持久化。我刚开始就踩了这个坑,跑了半天发现历史记录全丢了。