
1. 从一场调度事故说起先讲个我自己的真事。前两年给一家零售客户做数据平台重构他们原来的调度系统是自研的一个 Java 服务挂了个 Quartz再加一堆 Shell 脚本拼凑。平时跑跑批还凑合但一到月初算销售报表十几个任务挤在一起有的重复跑、有的漏跑出错了只能靠人肉盯日志。有一次大促后的数据回刷一个上游任务失败导致下游五个报表全部错乱排查了整整一夜最后发现就是脚本里一个日期参数没对上。痛定思痛换上成熟的工作流编排工具之后类似的低级事故基本绝迹。但紧接着就遇到了下一道坎市面上叫得上名字的编排工具至少几十个Airflow、Prefect、Dagster、Temporal 这几个尤其常被拿来对比。很多团队在选型阶段就卡住了要么看别人用什么就抄什么要么在飞书群里吵一个月也拿不定主意。作为一个把 Airflow、Prefect、Dagster 都跑过生产也拿 Temporal 做过业务编排的实践者我今天不打算念官方文档就把这几个工具按“生产环境里的真实表现”逐层扒开说说它们各自的脾气、适配场景和那些文档里不会写的坑。2. 先问清三个问题再谈选型选型这件事最忌讳上来就比功能清单。功能再多跟你没关系也是白搭。我做技术选型有个习惯第一步从来不是看哪个工具最火而是先回答三个问题。第一你的任务是“定时跑批”还是“事件驱动”这是分水岭。如果你的核心场景是“每天凌晨两点跑数据同步同步完做清洗清洗完算指标指标算完发报表”那这是典型的定时批处理Airflow、Prefect、Dagster 都擅长。但如果你的任务是“用户下单后 24 小时内未支付要发提醒”“订单创建后依次调用库存、优惠、风控、支付四个服务”这属于事件驱动的长流程编排Temporal 要合适得多。第二你的团队擅长什么语言Airflow 和 Prefect 的 DAG 定义在 Python 里写但 Airflow 本质上是个“调度平台”真正的重活都落在 Operator比如 BashOperator、PythonOperator、KubernetesPodOperator上写起来像搭积木Prefect 则把工作流写成普通 Python 函数心智负担低不少对纯 Python 团队非常友好。Dagster 也是 Python 为主但它更进一步连数据资产的概念都引入了写起来更像在“定义数据产品”。Temporal 是真正的多语言 SDKJava、Go、Python、TypeScript 都有官方支持如果你团队主力是 Java/Go选 Temporal 是顺理成章的。第三你更怕维护成本高还是更怕功能不够用这四个工具里Airflow 的维护成本是最高的调度器、Web 服务、数据库、执行器一套东西都得运维。Prefect 2.x 之后把很多东西简化了尤其自托管的 Prefect Server 比 Airflow 轻得多。Dagster 在运维上也有意做了简化但核心概念学起来比 Prefect 陡峭一些。Temporal 的部署关系到没那么复杂但如果你要用好它的持久化工作流得对“Event History”和“Deterministic 约束”有一定的理解门槛。说白了选型不是选“最好的”而是选“最合适你当前状况的”。接下来的篇幅我把每个工具的脾气都摸一遍。3. 逐个拆解它们到底是什么思路3.1 Airflow调度界的“老大哥”也是“重剑无锋”Airflow 出自 Airbnb2015 年开源后来捐给了 Apache。它解决的核心问题非常简单有一堆任务要按指定顺序、指定时间跑任务之间还有依赖关系。Airflow 用 DAG有向无环图这种结构来建模天然契合“ETL 流程分步骤执行”的直觉。在生产环境里Airflow 我见过跑得最稳的形态是用 KubernetesExecutor 远程日志 独立 PostgreSQL 元数据库。每个 DAG 的每个 Task 都可以动态地起一个 Pod 来执行任务之间环境隔离日志统一打到 S3 或阿里云 OSS元数据库单独部署不跟 Airflow 主进程抢资源。这套架构能吃下每天几千上万个 Task 的调度量。但它有几个让人牙痒痒的地方。一是调度器单点扩展需要技巧。Airflow 1.x 时期的 scheduler 是单进程的到了 2.0 之后支持了多 scheduler但配置不当容易出现“任务 tuple 被重复调度”或者“心跳超时”的诡异问题。我踩过一次两个 scheduler 实例同时启动结果同一个 DAG run 的同一个 task 出现在两个不同的 worker 上重复执行了一次数据写入那张表的数据翻了倍。二是DAG 代码和业务代码是强耦合的测试起来麻烦。官方推荐在 DAG 里写 idempotent 逻辑但现实项目里有的同事写的 Python 函数就是有状态的一旦 Airflow 重跑历史任务就可能跑出不一样的结果。三是动态 DAG 生成是坑。有的人为了少写重复代码会用一个脚本去动态扫描一张配置表然后按配置自动生成一堆 DAG。这个思路本身没问题但一旦配置表变更频繁、生成逻辑写得不严谨DAG 列表会闪变调度器扫描目录时容易“串戏”。所以 Airflow 适合什么场景我总结为定时批任务为主、依赖关系清晰、团队能接受一定运维成本、生态要用到大量现成 Operator尤其是云厂商服务比如 AWS S3、Redshift、Snowflake 等的团队。它不是最优雅的但它是目前“生态最全、踩坑资料最多、招人最容易”的选择。3.2 Prefect把“开发者体验”做成了卖点的后起之秀Prefect 是 2018 年出来的项目创始团队本身对 Airflow 的痛点有很深的体会。如果说 Airflow 的定位是“调度平台”Prefect 的定位更像是“让工程师用写普通代码的方式写工作流”。我印象最深的一点是Prefect 2.x 里你不需要把任务写成一个单独的 Operator 类也不需要显式写 PythonOperator(task_idxxx, python_callableyyy)直接一个装饰器就完事了。from prefect import task, flow task(retries2, retry_delay_seconds60) def fetch_data(date: str): # 拉数逻辑 return df flow def daily_pipeline(date: str): data fetch_data(date) clean_data(data) write_report(data) daily_pipeline(2025-01-01)这套写法的好处是Python 开发者零学习成本上手。它把 DAG 这种抽象藏得很深你写的就是一个个函数函数之间谁来先谁后由调用关系自然决定。对“刚从脚本搬过来的团队”非常友好。但 Prefect 也有它的脾性我讲几个真实体验。Prefect 2.x 的引擎是“动态工作流引擎”它的状态管理比 Airflow 更灵活但也更容易让人困惑。比如一个 flow 跑了很久突然手动取消某些 task 还是会继续跑完才“感知”到被取消这跟 Airflow 里 kill 一个 task instance 的体验不太一样。Prefect 的Concurrency Limit 和 Rate Limit配置在自托管模式下有点“智障”。默认情况下你跑到高并发时任务会积压在队列里但你从 UI 上看到的是“Pending”状态没法直观看到到底是卡在什么环节。这个问题后来版本有所优化但自托管用户还是要多看 agent 日志不能太依赖 UI。服务端那一端Prefect 2.x 的Prefect Server 是单体的包含 API、UI、数据库迁移逻辑等部署起来比 Airflow 轻不少但生产环境你必须注意它的数据库表会膨胀建议定期清理 flow run / task run 的历史数据。Prefect 特别适合数据团队规模不大、不想养专职 Airflow 运维、业务以 Python 为主、需求迭代快的场景。它把“写工作流”这件事的成本降得很低尤其适合“小步快跑”的数据开发风格。3.3 Dagster面向“数据资产”的编排重新定义可观测性Dagster 被很多人视为Airflow 的现代化继任者。它的核心概念不是“任务”也不是“DAG”而是“数据资产Data Asset”。听起来抽象我举个实际例子你就懂了。你用 Airflow 写一个 ETL 管道思路是任务 A - 任务 B - 任务 C。你用 Dagster 写同样的管道思路会变成表 raw_orders - 表 dim_customer - 报表 daily_sales。也就是说Dagster 让你把注意力从“步骤”转移到“数据本身”。这个转变在生产里有非常实际的价值。比如你的数据管线里有一张表挂了用 Airflow你只能看到是哪一个 DAG 的哪一步失败了用 Dagster资产依赖图直接展示这张表的上游和下游是谁哪个资产的更新被阻塞就能快速定位。团队里如果有数据质量测试、数据血缘、数据治理这类需求Dagster 的视野比 Airflow 高了一档。Dagster 的软件定义资产Software-defined Asset模型做起来比写传统 DAG 要“绕”一点。你得先习惯把产出物表、模型、报表当作一等公民来定义再让计算逻辑去“刷新”这些资产。这跟绝大多数人“面向函数”的编码直觉是反着的所以上手曲线比 Prefect 更陡。我有一次给一个团队做 Dagster 的培训大部分人是数据仓库工程师习惯写 SQL 和处理数据表。他们在听了 asset 概念之后非常兴奋因为终于有个工具能把数据表之间的关系直接可视化了。但等真到了写代码阶段不少人还是习惯性地先想到“我的步骤是什么”而不是“我的资产是什么”。这是一个认知转换的过程得花时间适应。Dagster 在运维上的表现也可圈可点。它的Daemon守护进程负责调度、传感器、运行监控比 Airflow 的 scheduler 要轻一些。它也支持 Kubernetes 模式Job 可以按 asset 粒度跑没必要的话不用为每个 task 起一个 Pod。在资源利用效率上比 Airflow 的“一 task 一 pod”模式要划算。生产环境里我建议团队满足以下条件再上 Dagster你对数据血缘、数据质量、数据可观测性有明确诉求团队里至少有一个人愿意花时间深入理解资产模型数据平台不以 Airflow 现存 DAG 为主或者你愿意做一次完整的迁移。如果你只是想要一个 Airflow 平替建议 Directly 去用 Prefect如果你想把“数据资产”这个概念真正落地那 Dagster 是这几个工具里最值得投入的方向。3.4 Temporal做业务长流程编排的“状态机大师”前面三个工具的基因都是“批处理/ETL 调度器”Temporal 压根不是这个流派。它源自 Uber 的 Cadence后来独立出来成了 Temporal。它解决的是“分布式应用中的状态管理”问题比“跑批任务”要底层得多。很多人第一次接触 Temporal 会很懵它跟 Airflow 有什么可比性答案是如果你的编排对象不是“数据任务”而是“业务流程”那 Temporal 的优势就会碾压前三个。举例。你们做一个电商系统用户下单后要经历预扣库存 - 调用优惠券服务 - 调用支付网关 - 支付结果回调 - 通知仓储发货。在这条链路里每个服务都是独立的网络可能超时服务可能重启支付回调可能延迟几个小时甚至几天。如果用传统同步调用的方式写一旦中途某个服务挂了整个流程就卡死在那里。Temporal 的做法是你写的工作流代码Workflow会被持久化到 Temporal Server工作流每一步执行完后事件历史会记录在数据库里如果进程崩溃它可以从最近的事件点恢复执行而不是从头再来。这种“持久化的工作流执行引擎”能力是 Airflow 系工具完全不具备的。我举个更贴近实战的例子。我之前帮一个做供应链金融的团队改造过他们的贷款审批流程。流程是这个样子的提交申请 - 调用征信接口 - 人工审批 - 若通过则签约 - 等待放款方回调 - 放款后定时查账户状态。这个流程的等待时间可能长达数日完全不是“定时批处理”能覆盖的。用 Temporal 实现后核心逻辑就是一个 Workflow 函数中间的所有等待事件都通过Workflow.await/workflow.wait_condition实现。关键点是工作流代码必须得是确定性的deterministic你不能在 Workflow 函数里直接调用外部 HTTP API 或者用随机数否则恢复执行时会产生不同的结果。外部调用必须包在 Activity 里。Temporal 的代码风格大致是下面这个感觉from temporalio import workflow from temporalio.common import RetryPolicy workflow.defn class LoanApplicationWorkflow: workflow.run async def run(self, application_id: str): # 1. 调外部征信服务(activity) credit_result await workflow.execute_activity( check_credit, application_id, start_to_close_timeouttimedelta(seconds30), retry_policyRetryPolicy(maximum_attempts3), ) # 2. 人工审批等待事件 await workflow.wait_condition(lambda: self.approval_received) # 3. 执行签约 await workflow.execute_activity(sign_contract, application_id) # 4. 等待放款回调 await workflow.wait_condition(lambda: self.payment_received) # 5. 后续操作 await workflow.execute_activity(track_repayment, application_id)看着是不是特别像“普通业务代码”?对。这就是 Temporal 的设计哲学把你的业务逻辑写得像单机代码一样简单剩下的状态持久化、重试、恢复、定时全交给引擎。当然用 Temporal 也有代价。它不是一个“开箱即用专为数据处理设计”的工具官方对数据批处理场景支持较弱比如 Data Pipeline 里的 daily run 语义、依赖管理、run id 对应关系等都要你自己封装。如果只是拿来做调度 cron那大材小用也没发挥出优势。它对运维的要求主要体现在需要多维护一个 Temporal Server支持自托管或者用 Temporal Cloud。它的杀手级应用场景是微服务编排、业务流程自动化、需要长时间等待外部事件、有严格事务性和可恢复性要求的业务系统。如果你发现自己写了一堆配套数据库表来记录“每个业务流程走到哪一步”且为了“重启后能恢复现场”写了一堆状态机代码那大概率就是需要 Temporal 了。4. 生产环境正面硬刚一张表看懂关键差异前面讲了很多“气质”上的差异到了真要选型的时候还是得落到具体指标上。我这里列一个从生产实践中提炼出来的对比表覆盖我踩过坑且认为最重要的维度而不是官方文档里的泛泛介绍。维度AirflowPrefectDagsterTemporal核心抽象DAG / TaskFlow / TaskAsset / JobWorkflow / Activity编程语言Python生态广PythonPython有 GraphQL APIJava / Go / Python / TypeScript调度模式定时cron / data interval为主定时 事件sensor / webhook 都可定时 传感器asset sensor定时也是 Workflow但核心是事件驱动重试机制任务级 retries可配指数退避装饰器内直接配体验好任务级和 asset 级支持Activity 级 RetryPolicy交互丰富长时间等待不支持靠 sensor 轮询支持 defer / wait但偏批处理主要通过传感器轮询不适合长等待原生支持 Workflow.await事件驱动部署形态Scheduler Web Worker 元数据库Server Agent或单进程Daemon Web 执行宿主支持 K8sTemporal Server前端 后端 可视化多语言 SDK主要 PythonJava 支持较晚Python 为主其他语言较弱Python 为主多语言一等公民UI / 可观测性成熟但任务级视图较重UI 清爽但市面资料较少资产血缘图非常出彩事件历史视图极强适合排查问题最常见生产问题scheduler 性能、DB 膨胀、task 重复跑agent 连接、并发控制、UI 状态滞后asset“资产定义”与血缘更新包导致视觉复杂工作流 event history 过大、版本兼容适配人群传统数仓团队、大量现成 Operator小而精的数据团队、Python 原生对数据质量和血缘有强诉求的团队微服务团队、处理业务状态流这张表别当“通关秘籍”看核心是帮你把讨论焦点从“谁更高级”拉回到“谁更匹配”。5. 选型决策我的七步判断法网上那么多所谓的选型方法论大多是罗列一堆维度然后说“看你们需要”。我在这分享一个更实用的“排除法 场景验证”的组合流程。第一步圈定你最少要支撑的主场景。拿纸笔列出未来六个月核心要做的事。如果全是“每天凌晨跑批”直接排掉 Temporal如果核心是“订单流程状态管理”直接排掉 Airflow、Prefect、Dagster。第二步确认部署环境。你用自建 K8s 还是云厂商托管Airflow 和 Dagster 在 K8s 上都有成熟的 Helm 包Prefect 的 Helm 相对简单但企业级功能很多要靠 Prefect CloudTemporal 也有 Helm 包但生产级自托管涉及数据库、ES可选项、前端和后端多组件运维门槛是这里面最高的。第三步看团队技能栈。主力是 Java/Go 直接考虑 Temporal主力是 Python 才进入 Airflow / Prefect / Dagster 三选一。第四步评估“重跑历史数据”的频率。数仓经常要做数据回刷Airflow 的 catchup / backfill 是史上最成熟的功能你用起来会非常放心。Prefect 在 2.x 里也有 rerun体验略好但社区资料不如 Airflow 多。Dagster 有 materialize 概念也可以回溯刷新资产但需要适应它的“状态世界”。第五步设想六个月后的运维现场。如果你的团队里没有专人维护调度系统Airflow 这种“全能选手”会拖慢你发布速度。Prefect 的自托管省心一些但排查问题还是经常要去看 API 日志和 agent 日志。Dagster 也是同理架构虽然简洁但概念理解门槛带来的“运维错觉”可能导致问题难定位。第六步搞清楚未来的扩展方向。如果公司规划是大规模机器学习平台Airflow 生态里一堆 ML 相关的 operator你可能更容易找到参考如果是要做 Data Mesh / 数据产品目录Dagster 一听就适合如果在酝酿微服务治理平台Temporal 未来会更丝滑。第七步不要混用除非有强理由。有的团队会同时上 Airflow 和 TemporalAirflow 管数仓批任务Temporal 管业务流。这个是可行的但你得接受两套系统的运维成本。如果你只想要一套搞定一般来说会在 Airflow 和 Temporal 之间选一个作为主导其他用来补充。6. 避坑实录这些坑我都替你踩过坑一Airflow 的时区问题会共同导致 DAG 错跑。很多团队在追查“为什么我的 DAG 晚了一个小时才跑”的时候第一反应是怀疑 crontab 配置最后发现是 Airflow 的default_timezone与execution_date的语义没搞清楚。Airflow 2.x 里时区默认是 UTC如果你期望的是“每天 8 点北京时间跑”得在 DAG 参数里写schedule0 0 * * *并设置timezone为你所在城市。注意execution_date是计划开始时间不是真正跑起来的时间在重跑和回溯时容易看花眼。坑二Prefect 的 flow 里别用全局变量存状态。Prefect 2.x 是基于 Python 异步模型设计的一个 flow 可能在多个进程中执行或者状态被序列化到数据库。如果你在模块里写global state或者依赖了不可序列化的对象比如数据库连接在某些执行进程下会莫名报错。最稳妥的姿势是所有可变状态都通过 return 传递或者用 Prefect 官方推荐的 Block 和 Variable 机制。坑三Dagster 的“asset 之间依赖”太灵活了反而不容易约束团队。我见过 DAG 里正交的资产互相引用导致一张“资产依赖图”最终变成“意大利面条”。原因往往是团队缺乏 Data Contract 意识。建议在引入 Dagster 之前先明确定义好每张表的所有者、更新级别和质量负责人再把这些约束写进工程规范和 CI 流程里。坑四Temporal 的 Workflow 代码里绝对不能用非确定性操作。像datetime.now()、random.random()这类函数在 Workflow 里执行会触发非确定性错误服务端恢复 replay 时对不上事件历史直接让 workflow 处于 failed 状态。外部时间、随机数、外部 API 调用统统放到 Activity 里。这条规则一定要写进团队的 Code Review CheckList 里否则上线之后苦不堪言。坑五无论选哪个都要做“可观测性”专项。Airflow 提供日志但缺乏 metricsPrefect 的 UI 提供 pretty 的视图但指标项不算多Dagster 的可观测性很强但偏重数据管线Temporal 的 metrics 丰富但需要自己接 Prometheus/Grafana。生产环境建议开头就把“任务成功率、运行时长、重试次数、积压数”这些指标画到监控大盘上。没监控等于裸奔。7. 工程实践之外的一点个人想法在真正动手落地前你完全可以拿一个 demo 项目同时跑一遍这四个工具不用全都装而是按我前面列的那些“核心场景”各写一个最小的例子让 Airflow 跑一个 Excel 数据同步、让 Prefect 监听一个 Webhook 触发、让 Dagster 维护一张资产表并做 lineage 展示、让 Temporal 跑一个“等待用户确认后再做后续操作”的流程。做完这些演示你会发现其实“谁更适合你”这个答案你自己心里就有数了根本不需要操心别人怎么站队。我还想特别提醒一个容易忽略的点工具是拿来解决问题的不是拿来证明自己厉害的。我见过不少团队选型时优先考虑简历含金量、技术时髦值之类结果把系统迁到 Dagster 或 Temporal 之后发现团队连一个能独立排查问题的人都没有最后运行质量反而还不如原来简单的 Shell 定时任务。技术选型从来不只是技术问题它牵涉到组织能力、人员水平、长期维护策略甚至团队士气。如果让我给一个最简单粗暴的建议你是数据团队、只想稳定跑批且团队熟悉 PythonAirflow 依然是默认选项你希望降低维护成本、提升开发效率Prefect 值得赌一把你对数据质量和血缘有系统性诉求Dagster 可以上但别操之过急你在写业务系统、且对状态一致性有硬要求认真研究 Temporal。选型不是信仰之争是一场基于现状的务实投资。8. 落地执行清单最后给你一份可操作清单照着做至少不会跑偏先和团队一起做“场景清单”明确核心槽点。不允许有人只凭“听说”投票。拉一个三天的选型验证周期让团队里每个人至少写一个 demo覆盖从安装部署到运行的完整链路。拿最复杂的一条现有管道做迁移演练评估改动成本和时间。确认生产环境的 K8s 资源配额和网络存储方案避免低估运维负载。设定上线后的核心监控指标比如调度延迟、失败率、重试分布。写一份团队内部的《工作流开发规范》把动态 DAG、状态存储、异常处理、无法达到确定性操作等这些坑提前声明掉。不要一下子把老系统里的所有任务全量迁移先并行跑一个月验证稳定性后再逐步切流。工具终究只是手段你真正交付的价值是“任务稳定跑、流程不出错、业务能复用”。走到那一步用什么工具都是顺手的事。