spark - 执行顺序

5 阅读2分钟

下面是一个详细的例子,基于 rdd1.join(rdd2).groupBy().filter() 语句,解释如何构建 DAG、DAG 的结构、任务划分、分区情况,以及如何将任务发送到工作节点。

示例数据

假设我们有两个 RDD:

  • rdd1: 包含学生 ID 和姓名的键值对
  • rdd2: 包含学生 ID 和成绩的键值对
rdd1 = sc.parallelize([(1, "Alice"), (2, "Bob"), (3, "Charlie")])
rdd2 = sc.parallelize([(1, 85), (2, 90), (3, 75), (1, 95)])

操作

执行以下操作:

result = rdd1.join(rdd2).groupBy(lambda x: x[0]).filter(lambda x: x[1][0] > 80)

1. 构建 DAG

在执行 joingroupByfilter 时,Spark 会构建一个 DAG。DAG 的结构如下:

   +--------+          +--------+
   |  rdd1  |          |  rdd2  |
   +--------+          +--------+
        \               /
         \             /
          \   join    /
           +---------+
           |   rdd3   |
           +---------+
                |
            groupBy
                |
           +---------+
           |   rdd4   |
           +---------+
                |
             filter
                |
           +---------+
           |  result  |
           +---------+

2. 阶段划分

根据操作的依赖关系,DAG 被划分为以下阶段:

  • 阶段 1:执行 join

    • 这是一个宽依赖操作,涉及到两个 RDD 的重分区。
  • 阶段 2:执行 groupBy

    • 也是一个宽依赖操作,基于 join 的结果进行分组。
  • 阶段 3:执行 filter

    • 这是一个窄依赖操作,可以在同一阶段内完成。

3. 任务划分

假设 rdd1rdd2 都有 2 个分区,DAG 将被划分为如下任务:

  • 任务 1(阶段 1):

    • 对于 rdd1rdd2 的每个分区,进行 join 操作。
    • 任务 1.1:处理 rdd1 的第一个分区和 rdd2 的第一个分区。
    • 任务 1.2:处理 rdd1 的第一个分区和 rdd2 的第二个分区。
    • 任务 1.3:处理 rdd1 的第二个分区和 rdd2 的第一个分区。
    • 任务 1.4:处理 rdd1 的第二个分区和 rdd2 的第二个分区。
  • 任务 2(阶段 2):

    • join 结果进行 groupBy,每个分区的结果都将产生新的分区。
  • 任务 3(阶段 3):

    • groupBy 的结果进行 filter 操作。

4. 发送到工作节点

  • Spark 的 Driver 会将任务分配给可用的 Worker 节点。
  • 每个 Worker 节点会并行执行任务,处理其分配的分区。
  • 计算结果会被返回到 Driver,或者写入外部存储。

总结

通过上述步骤,Spark 将 rdd1.join(rdd2).groupBy().filter() 的操作转化为一个 DAG,并根据操作的依赖关系划分为多个阶段和任务。每个任务在不同的工作节点上并行执行,最终得到计算结果。