DAG、Stage、Task、Shuffle:分布式计算的基本模型

分布式计算引擎通常把作业表达成 DAG,再切成 Stage 和 Task 并行执行,Shuffle 则是性能和稳定性最关键的成本来源。

DAG、Stage、Task、Shuffle:分布式计算的基本模型

无论 Spark 还是 Flink,底层都离不开一个思想:把复杂计算拆成可以并行执行的小任务。

一个 SQL 查询或数据处理任务,会被转换成计算图。图里的节点是算子,边表示数据依赖。这个图通常是 DAG,也就是有向无环图。

Rendering diagram...

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 执行计划,才知道作业到底卡在哪里。

参考链接