Spark RDD各种join算子从源码层分析实现方式

在 Spark RDD 中,joinleftOuterJoinrightOuterJoinfullOuterJoin 等多个 Join 操作符都使用了 cogroup 进行底层实现。cogroup 是 Spark 中的一种底层分组操作,可以将两个或多个 RDD 中同一键的数据分组到一起,为各种 Join 操作提供了基础。下面我们从源码实现角度来分析这些 Join 操作符的实现原理,并列出相关的核心代码。

1. join

join 是最常用的连接操作,它会返回两个 RDD 中键相同的元素对。join 操作底层依赖 cogroup 实现:

scala 复制代码
def join[W](other: RDD[(K, W)], numPartitions: Int): RDD[(K, (V, W))] = {
  this.cogroup(other, numPartitions).flatMapValues {
    case (vs, ws) => for (v <- vs.iterator; w <- ws.iterator) yield (v, w)
  }
}

实现过程

  1. cogroup 将两个 RDD 中相同的键的数据组合成 (K, (Iterable[V], Iterable[W])) 形式。
  2. 然后通过 flatMapValues 遍历所有相同键的值对 (v, w),形成最终的 (K, (V, W)) 格式。

示例

python 复制代码
rdd1 = sc.parallelize([("a", 1), ("b", 2)])
rdd2 = sc.parallelize([("a", 3), ("b", 4)])
joined = rdd1.join(rdd2)
print(joined.collect())
# 输出: [('a', (1, 3)), ('b', (2, 4))]

2. leftOuterJoin

leftOuterJoin 会返回左侧 RDD 的所有键,并对右侧 RDD 中匹配的键进行连接,未匹配到的则返回 None

scala 复制代码
def leftOuterJoin[W](other: RDD[(K, W)], numPartitions: Int): RDD[(K, (V, Option[W]))] = {
  this.cogroup(other, numPartitions).flatMapValues {
    case (vs, ws) =>
      if (ws.isEmpty) vs.iterator.map(v => (v, None))
      else for (v <- vs.iterator; w <- ws.iterator) yield (v, Some(w))
  }
}

实现过程

  1. 使用 cogroup 分组得到 (K, (Iterable[V], Iterable[W])) 格式。
  2. 对于左侧 RDD 的值 vs,如果右侧 RDD 的 ws 为空,则返回 None;否则返回 (v, Some(w))

示例

python 复制代码
rdd1 = sc.parallelize([("a", 1), ("b", 2)])
rdd2 = sc.parallelize([("a", 3)])
left_joined = rdd1.leftOuterJoin(rdd2)
print(left_joined.collect())
# 输出: [('a', (1, Some(3))), ('b', (2, None))]

3. rightOuterJoin

rightOuterJoin 返回右侧 RDD 的所有键,未匹配的左侧 RDD 值则为 None

scala 复制代码
def rightOuterJoin[W](other: RDD[(K, W)], numPartitions: Int): RDD[(K, (Option[V], W))] = {
  this.cogroup(other, numPartitions).flatMapValues {
    case (vs, ws) =>
      if (vs.isEmpty) ws.iterator.map(w => (None, w))
      else for (v <- vs.iterator; w <- ws.iterator) yield (Some(v), w)
  }
}

实现过程

  1. cogroup 将两个 RDD 分组。
  2. 对右侧 RDD 中的值 ws,如果左侧 RDD 中 vs 为空,则返回 None;否则返回 (Some(v), w)

示例

python 复制代码
rdd1 = sc.parallelize([("a", 1)])
rdd2 = sc.parallelize([("a", 3), ("b", 4)])
right_joined = rdd1.rightOuterJoin(rdd2)
print(right_joined.collect())
# 输出: [('a', (Some(1), 3)), ('b', (None, 4))]

4. fullOuterJoin

fullOuterJoin 会返回左右两个 RDD 的所有键。未匹配的键对应的值为 None

scala 复制代码
def fullOuterJoin[W](other: RDD[(K, W)], numPartitions: Int): RDD[(K, (Option[V], Option[W]))] = {
  this.cogroup(other, numPartitions).flatMapValues {
    case (vs, ws) =>
      if (vs.isEmpty) ws.iterator.map(w => (None, Some(w)))
      else if (ws.isEmpty) vs.iterator.map(v => (Some(v), None))
      else for (v <- vs.iterator; w <- ws.iterator) yield (Some(v), Some(w))
  }
}

实现过程

  1. 使用 cogroup 进行键的分组。
  2. 对于每个键的 vsws,分别判断它们是否为空,如果为空则返回 None

示例

python 复制代码
rdd1 = sc.parallelize([("a", 1), ("b", 2)])
rdd2 = sc.parallelize([("a", 3), ("c", 4)])
full_joined = rdd1.fullOuterJoin(rdd2)
print(full_joined.collect())
# 输出: [('a', (Some(1), Some(3))), ('b', (Some(2), None)), ('c', (None, Some(4)))]

cogroup 的作用

cogroup 是 Spark 中连接操作的核心,通过对两个或多个 RDD 进行键分组,将相同键的数据放到一个集合中,使得不同类型的 Join 操作能够灵活实现。通过 cogroup,Spark 可以高效地将键相同的值合并在一起,而不需要在每个连接操作中重写分组逻辑。

总结

  • 底层共用 :所有 join 操作都使用 cogroup 进行分组,然后通过对分组后的键值对进行遍历和组合来实现不同的 Join 类型。
  • 性能优化cogroup 提供了键值对的高效分组机制,减少了 Join 操作中的数据传输量,从而提升了连接操作的性能。

这几种 Join 操作的实现逻辑和底层调用可以帮助我们理解 Spark 在大数据处理中如何高效实现连接操作。

相关推荐
用户3610588626124 小时前
SparkStreaming 之配置参数详解
大数据·spark
用户3610588626125 小时前
SparkStreaming 之 Direct 模式并行度设置与 offset 管理
大数据·spark
BYSJMG6 小时前
计算机毕业设计选题推荐|【基于大数据的植被光谱特征与环境因子关联分析及可视化】Spark+K-Means
大数据·python·信息可视化·数据分析·spark·kmeans·课程设计
阿里云大数据AI技术21 小时前
EMR Serverless Spark多模态 Daft 算子市场免费公测 | 10分钟极速实战(附视频教程)
人工智能·spark
IT研究室1 天前
最新大数据毕业设计选题推荐-基于大数据的京东商品销售分析与可视化的设计与实现-大数据-Spark-Hadoop-Bigdata
大数据·spark·课程设计
DeepLink_20252 天前
Triton第五课:分组矩阵乘调优
算子·infra·技术科普
用户3610588626122 天前
SparkStreaming 之 Driver HA 原理及搭建实操
大数据·spark
腾讯云大数据2 天前
AI Native数据湖的Spark+Ray一体化实践
大数据·人工智能·spark·腾讯云·腾讯云大数据
weixin_307779132 天前
Databricks里用PySpark统计指定表和字段中各字段的空值、空字符串或零值比例
运维·数据仓库·python·spark·云计算