1. 引言:为什么要理解RDD Pipeline?
在Spark中,RDD的转换操作通常不会立即执行,而是构建出一个计算链路。这种“惰性求值”的特性是Spark性能优化的基石。理解RDD Pipeline,也就是RDD的依赖关系和Stage划分机制,能帮助我们编写更高效的Spark程序,有效避免数据倾斜和重复计算。
2. RDD的依赖关系:血缘的两种形态
RDD的每个分区都由其父RDD通过特定的操作转换而来,这种父子关系称为“依赖”。Spark中定义了两种核心依赖:
2.1 窄依赖
父RDD的每个分区至多被子RDD的一个分区使用。例如map、filter、union等操作产生窄依赖。在窄依赖中,数据不需要跨节点移动,可以流水线化执行。
2.2 宽依赖
父RDD的每个分区会被子RDD的多个分区使用。典型的如groupByKey、reduceByKey、join等操作。宽依赖会导致Shuffle,即将数据按照Key的哈希值重新分发到不同节点,这个过程涉及大量的网络和磁盘IO。
3. Stage划分:Pipeline的物理边界
当遇到一个Action操作时,Spark DAGScheduler会根据RDD的依赖关系将作业划分成不同的Stage。划分的核心原则是:遇到宽依赖就进行Stage切分。在一个Stage内部,所有的窄依赖可以合并为一条流水线,数据在内存中依次通过多个算子,无需落地磁盘,这极大地提高了效率。
4. 窄依赖与宽依赖对比
下表系统对比了窄依赖和宽依赖的关键区别,有助于在实际编码中做出判断:
| 对比维度 | 窄依赖 | 宽依赖 |
|---|---|---|
| 计算特征 | 流水线计算,无需Shuffle |