01学完你能做到
- 区分调度、编排、DAG、任务、运行和分区。
- 说明 Airflow 与 Dagster 的主要设计差异。
- 为行情流水线设计失败重试、分区重跑和历史回补。
02这课解决什么问题
收盘后要等数据到齐,再聚合、校验、发布;某天数据迟到或某一步失败时,怎样只重跑受影响的日期,而不是整条链路从头开始?
03核心概念
调度决定什么时候跑,例如工作日某时刻、每隔几分钟或上游文件到达后。编排决定任务顺序、依赖关系和失败处理。依赖关系通常表示为有向无环图(DAG),箭头表示先后关系,不能形成环。
Airflow 以流程和任务为中心:DAG 是流水线定义,Task 是步骤,Dag Run 是一次运行。按日运行时要区分逻辑日期、数据区间和实际启动时间;处理某个交易日的数据,不等于任务启动的那一刻就是该交易日。
Catchup 表示是否为过去缺失的数据区间创建运行;Backfill 表示主动为历史日期或分区创建运行。重试适合网络抖动、上游暂时不可用等临时故障,不适合数据口径错误或输入根本不存在的情况。
Dagster 以资产为中心:你声明要产出的表、文件或其他资产,以及它们的上游依赖;分区把资产按日期或其他维度切开,回补就是选择一批分区重新物化。
一句话比较:Airflow 先想“要跑哪些任务”,Dagster 先想“要产出哪些数据资产”。
04在行情链路中的位置
交易日历 → 判断是否交易日 → 等待上游到齐
→ 落地原始数据 → 聚合 / 复权 → 质量校验
→ 写发布分区 → 刷新缓存、记录血缘调度系统本身不负责业务计算,它负责把各环节串起来。真实链路应按交易日历运行,而不是只用周一到周五的 cron;节假日和临时休市必须跳过。
05可重跑的最小设计
from pathlib import Path
import pandas as pd
def build_market_total(day, landing, output):
source = Path(landing) / f"bars_{day}.csv"
if not source.exists():
raise RuntimeError(f"{day} 的输入还未到达")
df = pd.read_csv(source)
result = (df.assign(market=df["code"].str[-2:])
.groupby("market", as_index=False)["turnover"]
.sum())
# 按日期覆盖写,而不是每次追加,确保同一日期重跑不会重复。
target = Path(output) / f"turnover_{day}.csv"
temp = target.with_suffix(".tmp")
result.to_csv(temp, index=False)
temp.replace(target)任务读取指定日期的输入、写入指定日期的输出;不要在回补历史时读取“当前最新数据”。写出时使用临时文件再原子替换,或使用数据库事务,避免任务中途留下半份结果。
06常见误区
- 任务里读取“最新数据”,导致同一日期重跑时输入已变,结果不可复现。
- 用 cron 的
1-5代替交易日历,漏掉节假日和临时休市。 - 混淆逻辑日期、数据区间和实际运行时间,造成差一天。
- 把重试当万能药,反复重试缺失数据或错误口径。
- 回补大量历史时不限制并发,打满上游和数据库。
- 结果使用追加写,重跑后出现重复记录。
07延伸阅读
08一分钟讲给别人
试着用自己的话讲一遍调度编排系统像后厨的厨师长:先等材料到齐,再按顺序加工,失败了重试或告警,哪天迟到就只重做哪天。Airflow 以任务流程为中心,Dagster 以数据资产为中心。无论使用哪个,任务都必须按指定日期读写,并做到重跑不重复、结果可复现。
5 道自测
先在心里答一遍,再展开对照。能说清理由,比答对数字更重要。
01调度和编排分别管什么?
02DAG 为什么不能有环?
03逻辑日期和实际运行时间有什么区别?
04什么情况下适合重试,什么情况下应直接失败告警?
05为什么任务不能读取“最新数据”?
已看 0/5 题
练动手练习
任务:为一个按交易日运行的两步流水线增加日期分区。模拟某天输入文件迟到:第一轮只让其他日期成功;文件补到后只重跑失败日期;最后重复重跑一个成功日期,检查输出内容和行数是否不变。
完成这一课
读完讲义、看完自测答案,这一课就算学完了。
- 读完讲义
- 看完全部自测
本课为投资者教育内容,案例数字均为示意,不构成任何投资建议,也不承诺收益。规则和费率会变,实际操作前请以交易所、监管机构和产品的最新公告为准。