1. DAG 基础概念:有向无环图的定义、拓扑排序原理、DAG 在任务调度中的核心价值
1.1 有向无环图的定义
有向无环图(Directed Acyclic Graph, DAG)是图论中的一种特殊结构,由一组顶点(Vertex)和一组有向边(Directed Edge)组成,且图中不存在任何环路。在任务调度与数据传输领域,DAG 是描述任务依赖关系与执行顺序的数学基础。
| 组成要素 | 符号表示 | 调度语义 |
|---|---|---|
| 顶点(Vertex) | V = {v₁, v₂, …, vₙ} | 一个计算任务、数据转换步骤或管道节点 |
| 有向边(Directed Edge) | E = {(vᵢ, vⱼ) | vᵢ → vⱼ} | vᵢ 必须在 vⱼ 之前完成,数据从 vᵢ 流向 vⱼ |
| 无环约束 | ∄ 路径 v₁ → v₂ → … → v₁ | 保证调度不会陷入死锁或无限循环 |
关键性质:DAG 的边方向代表依赖方向,而非数据流方向。在数据传输场景中,边方向通常与数据流方向一致,但在某些反向依赖模式中可能相反。
1.2 拓扑排序原理
拓扑排序(Topological Sorting)是将 DAG 的所有顶点排成一个线性序列,使得对于每条有向边 (u, v),u 在序列中都出现在 v 之前。这是 DAG 调度的核心算法基础。
两种经典实现算法:
- Kahn 算法(入度法):反复移除入度为 0 的顶点,将其加入排序结果,并移除其所有出边。
- DFS 后序法:深度优先遍历,在回溯时将顶点压入栈,最终栈的逆序即为拓扑序。
// Kahn 算法伪代码
function topologicalSort(graph):
inDegree = computeInDegree(graph)
queue = [v for v in graph.vertices if inDegree[v] == 0]
result = []
while queue is not empty:
u = queue.dequeue()
result.append(u)
for v in graph.outNeighbors(u):
inDegree[v]--
if inDegree[v] == 0:
queue.enqueue(v)
if len(result) != len(graph.vertices):
throw "Graph contains a cycle"
return result
工程提示:在分布式调度系统中,Kahn 算法更受欢迎,因为它天然支持并行调度——每次入度为 0 的顶点集合可以同时执行,无需等待全局排序完成。
1.3 DAG 在任务调度中的核心价值
DAG 之所以成为任务调度与数据传输的标准建模工具,源于以下四个核心价值:
- 依赖显式化:任务间的先后关系、数据依赖、资源约束通过有向边清晰表达,消除隐式耦合。
- 并行度最大化:拓扑排序自动识别无依赖的任务组,调度器可同时调度这些任务,充分利用集群资源。
- 容错与重试边界:DAG 的每个顶点是独立的失败域,失败任务仅影响其下游,调度器可精确重试或回滚。
- 可观测性:DAG 结构天然支持执行状态追踪、进度可视化、瓶颈分析。
行业共识:Apache Airflow、Apache DolphinScheduler、Google Workflow、AWS Step Functions 等主流调度系统均以 DAG 作为核心抽象。DAG 边界决定了调度系统的表达能力上限。
1.4 DAG 结构示意图
下图展示了一个典型的数据传输 DAG,包含 6 个任务节点和 7 条依赖边,体现分层依赖与并行执行模式:
1.5 拓扑排序与调度顺序
对上述 DAG 执行拓扑排序,可得到多个合法的执行序列。以下表格展示其中一种典型序列及其并行调度策略:
| 调度轮次 | 可执行任务 | 依赖条件 | 并行度 |
|---|---|---|---|
| 第 1 轮 | 数据源采集 | 无依赖(入度为 0) | 1 |
| 第 2 轮 | 清洗任务 A、清洗任务 B | 数据源采集完成 | 2 |
| 第 3 轮 | 格式转换、数据聚合 | 各自上游清洗任务完成 | 2 |
| 第 4 轮 | 合并输出 | 格式转换与数据聚合均完成 | 1 |
边界情况注意:当 DAG 中存在多个入度为 0 的顶点时,调度器必须考虑资源约束(如 CPU 核数、内存上限、网络带宽),不能无限制并行。DAG 边界设计需要同时考虑逻辑依赖与物理资源。
1.6 DAG 与数据传输的天然契合
在数据传输管道中,DAG 的边不仅表示依赖关系,还承载数据语义:
- 数据血缘:每条边记录数据的来源、转换操作与去向,支撑数据溯源与审计。
- 增量传输:DAG 支持部分节点增量执行,仅重新计算变更的上游节点,避免全量重跑。
- 背压传播:当下游节点处理速度低于上游时,DAG 结构允许背压信号沿边反向传播,实现流量控制。
总结:DAG 为任务调度提供了数学上严谨、工程上可扩展的建模框架。理解 DAG 的拓扑性质与调度语义,是设计高效、可靠数据传输系统的第一步。