DAG、Stage、Task、Shuffle:分布式计算的基本模型
分布式计算引擎通常把作业表达成 DAG,再切成 Stage 和 Task 并行执行,Shuffle 则是性能和稳定性最关键的成本来源。
DAG、Stage、Task、Shuffle:分布式计算的基本模型
无论 Spark 还是 Flink,底层都离不开一个思想:把复杂计算拆成可以并行执行的小任务。
一个 SQL 查询或数据处理任务,会被转换成计算图。图里的节点是算子,边表示数据依赖。这个图通常是 DAG,也就是有向无环图。
DAG 是什么
DAG 用来描述计算步骤之间的依赖关系。比如先读数据,再过滤,再按用户聚合,最后写结果。
有了 DAG,计算引擎就能知道哪些步骤可以并行,哪些步骤必须等待上游完成。
Stage 和 Task
Stage 是一组可以连续执行的计算步骤。遇到 Shuffle 这种需要重新分发数据的地方,通常会切分 Stage。
Task 是真正被调度到 Executor 或 TaskManager 上执行的最小单位。
Job
├── Stage 1
│ ├── Task 1
│ ├── Task 2
│ └── Task 3
└── Stage 2
├── Task 4
└── Task 5
Shuffle 为什么贵
Shuffle 是把数据按 key 重新分发到不同节点。比如 group by user_id,同一个用户的数据必须到同一个下游任务里聚合。
Shuffle 贵在网络、磁盘、序列化、排序和内存压力。很多 Spark 作业慢,不是慢在计算,而是慢在 Shuffle。
SELECT user_id, count(*) AS cnt
FROM events
GROUP BY user_id;
这条 SQL 看起来简单,但如果 events 很大,就会触发大量数据按 user_id 重分布。
宽依赖和窄依赖
窄依赖是上游一个分区只被下游少量分区依赖,例如 map、filter。
宽依赖是上游多个分区的数据会被多个下游分区重新组织,例如 group by、join、distinct。
宽依赖通常意味着 Shuffle,也意味着更高成本。
小结
DAG 决定计算结构,Stage 决定任务边界,Task 决定并行执行,Shuffle 决定性能瓶颈。
理解这几个概念,再看 Spark UI、Flink Web UI 或 SQL 执行计划,才知道作业到底卡在哪里。