1. 课程导论:什么是DAG任务调度?为什么需要边界拆分与依赖管理?
1.1 从一个现实问题开始
假设你正在构建一个数据管道:每天凌晨从多个数据源抽取数据,经过清洗、转换、聚合,最终写入数仓和报表系统。如果这些步骤串行执行,总耗时可能超过窗口期;如果并行执行,又可能因为上游数据未就绪而导致下游任务失败。更棘手的是,当某个任务失败时,你需要精确重跑其下游,而不是全量重跑。
这个场景的核心矛盾是:任务之间存在依赖关系,且执行顺序必须满足这些依赖。DAG(有向无环图)任务调度正是为解决这类问题而生。
关键洞察: 没有依赖管理的并行是混乱,没有边界拆分的调度是耦合。DAG 调度器的本质是将“任务执行”与“任务编排”解耦,让系统能够自动推导出正确的执行顺序。
1.2 什么是DAG任务调度?
DAG(Directed Acyclic Graph,有向无环图)是一种数学结构,由节点(Vertex)和有向边(Directed Edge)组成,且图中不存在环路。在任务调度领域:
- 节点:代表一个可执行的任务单元(如 Shell 脚本、Spark 作业、HTTP 调用)。
- 有向边:代表任务之间的依赖关系,方向从上游指向下游。
- 无环:保证调度不会陷入死循环,拓扑排序总能找到合法执行顺序。
一个典型的 DAG 调度流程如下:
- 用户定义任务节点及其依赖关系,提交 DAG 图。
- 调度器对 DAG 进行拓扑排序,生成执行序列。
- 调度器按拓扑顺序分发任务,并监控每个节点的完成状态。
- 当某个节点失败时,调度器根据依赖关系决定是否重试、跳过或终止下游。
常见误区: DAG 调度 ≠ 工作流引擎。工作流引擎通常包含人工审批、条件分支等复杂逻辑,而 DAG 调度更关注“自动化的依赖解析与并行执行”。
1.3 为什么需要边界拆分?
在大型系统中,一个 DAG 可能包含数百甚至数千个节点。如果不做边界拆分,会出现以下问题:
| 问题 | 后果 | 示例 |
|---|---|---|
| 单点故障扩散 | 一个任务失败导致整个 DAG 重跑 | 数据清洗节点失败,连带下游 50 个报表任务全部重跑 |
| 资源竞争不可控 | 并行度无法精细控制,集群资源被某个子图耗尽 | 两个无依赖的大数据任务同时申请 100 个 Executor,导致其他任务饥饿 |
| 运维粒度粗糙 | 无法单独暂停、重跑或监控某个子图 | 需要重跑“数据聚合”阶段,却只能重跑整个 DAG |
| 版本管理困难 | 修改一个子图需要重新部署整个 DAG 定义 | 调整“数据清洗”逻辑,却影响了“数据导出”的版本 |
边界拆分的核心思想是:将一个大 DAG 拆分为多个独立的子 DAG(Sub-DAG),每个子 DAG 拥有独立的生命周期、资源配额和错误处理策略。子 DAG 之间通过“契约接口”(如文件路径、消息队列、数据库状态)进行通信,而不是直接依赖内部节点。
1.4 为什么需要依赖管理?
依赖管理是 DAG 调度器的核心能力,它决定了调度系统能否正确、高效地执行任务。依赖管理需要解决三个层次的问题:
- 静态依赖:DAG 定义时的显式依赖,如“任务 B 必须在任务 A 完成后执行”。
- 动态依赖:运行时才能确定的依赖,如“任务 C 等待上游三个任务中任意两个完成即可执行”。
- 数据依赖:任务之间通过数据传递产生的隐式依赖,如“任务 D 需要读取任务 E 生成的分区数据”。
一个成熟的依赖管理系统应该具备以下能力:
| 能力维度 | 说明 | 典型实现 |
|---|---|---|
| 拓扑排序 | 自动推导出合法的执行顺序 | Kahn 算法、DFS 后序遍历 |
| 依赖解析 | 支持 AND、OR、N-out-of-M 等依赖逻辑 | 条件节点、依赖表达式 |
| 循环检测 | 在提交时或运行时检测环路并告警 | Tarjan 算法、着色标记法 |
| 依赖回溯 | 任务失败时自动回溯到上游依赖节点 | 依赖链追踪、因果分析 |
避坑指南: 不要试图在 DAG 调度器中实现“万能依赖”。过度复杂的依赖逻辑(如跨 DAG 的循环依赖、基于时间的动态依赖)会显著增加系统的不可预测性。建议将复杂依赖逻辑上浮到业务层,调度器只负责执行“确定性依赖”。
1.5 课程目标与学习路径
本课程的目标是帮助你掌握架构驱动的 DAG 任务调度服务设计方法,重点聚焦于边界拆分和依赖管理两个核心主题。完成本课程后,你将能够:
- 设计一个支持子 DAG 拆分、独立部署和独立运维的调度架构。
- 实现一个高性能的依赖解析引擎,支持多种依赖模式和循环检测。
- 掌握 DAG 调度中的常见设计模式(如分阶段调度、增量调度、回溯重跑)。
- 理解调度系统的高可用、可观测性和扩展性设计。
学习路径分为四个阶段:
| 阶段 | 章节范围 | 核心产出 |
|---|---|---|
| 基础篇 | 第 1-5 章 | 理解 DAG 调度核心概念,掌握拓扑排序和依赖解析算法 |
| 架构篇 | 第 6-15 章 | 设计边界拆分策略,实现子 DAG 调度器和跨 DAG 通信 |
| 进阶篇 | 第 16-25 章 | 实现动态依赖、增量调度、回溯重跑等高级特性 |
| 实战篇 | 第 26-30 章 | 构建一个生产级 DAG 调度服务,包含监控、告警和运维工具 |
1.6 DAG 调度核心架构图
下图展示了 DAG 任务调度服务的核心架构,重点突出边界拆分与依赖管理在整体系统中的位置:
1.7 本章小结
本章我们建立了 DAG 任务调度的基本认知框架:
- DAG 调度通过有向无环图模型,自动推导任务执行顺序,实现并行与依赖的平衡。
- 边界拆分将大 DAG 分解为独立子 DAG,解决单点故障扩散、资源竞争和运维粒度粗糙等问题。
- 依赖管理是调度引擎的核心能力,涵盖静态依赖、动态依赖和数据依赖三个层次。
- 本课程将按照“基础→架构→进阶→实战”的路径,系统性地讲解 DAG 调度服务的设计与实现。