Spark RDD中常用聚合算子源码层面的对比分析

在 Spark RDD 中,groupByKeyreduceByKeyfoldByKeyaggregateByKey 是常用的聚合算子,适用于按键进行数据分组和聚合。它们的实现方式各不相同,涉及底层调用的函数也有区别。以下是对这些算子在源码层面的分析,以及每个算子适用的场景和代码示例。


1. groupByKey

  • 功能 :将相同键的值分组,形成一个 (key, Iterable<values>) 的 RDD。

  • 源码分析
    groupByKey 底层使用了 combineByKeyWithClassTag 方法进行数据分组。

    scala 复制代码
    def groupByKey(): RDD[(K, Iterable[V])] = {
        combineByKeyWithClassTag(
          (v: V) => mutable.ArrayBuffer(v),
          (c: mutable.ArrayBuffer[V], v: V) => { c += v; c },
          (c1: mutable.ArrayBuffer[V], c2: mutable.ArrayBuffer[V]) => { c1 ++= c2; c1 }
        ).asInstanceOf[RDD[(K, Iterable[V])]]
    }
    • 适用场景:适合需要按键分组、无聚合的场景,但由于需要把所有键的值都传输到驱动端,数据量大时可能导致内存问题。
  • 示例

    python 复制代码
    rdd = sc.parallelize([("a", 1), ("b", 2), ("a", 3)])
    result = rdd.groupByKey().mapValues(list)
    print(result.collect())

    输出[('a', [1, 3]), ('b', [2])]


2. reduceByKey

  • 功能:基于给定的二元函数(如加法)对每个键的值进行聚合。

  • 源码分析
    reduceByKey 底层也是基于 combineByKeyWithClassTag 方法进行处理,但与 groupByKey 不同的是,它在每个分区内执行局部聚合,再进行全局聚合,减少了数据传输。

    scala 复制代码
    def reduceByKey(func: (V, V) => V): RDD[(K, V)] = {
        combineByKeyWithClassTag[V]((v: V) => v, func, func)
    }
    • 适用场景 :适用于需要对数据进行聚合计算的场景,能够有效减少 shuffle 数据量。
  • 示例

    python 复制代码
    rdd = sc.parallelize([("a", 1), ("b", 2), ("a", 3)])
    result = rdd.reduceByKey(lambda x, y: x + y)
    print(result.collect())

    输出[('a', 4), ('b', 2)]


3. foldByKey

  • 功能 :与 reduceByKey 类似,但提供了初始值,分区内和分区间合并时都使用这个初始值。

  • 源码分析
    foldByKey 的实现中调用了 aggregateByKey 方法,初始值会在每个分区中传递,确保聚合逻辑一致。

    scala 复制代码
    def foldByKey(zeroValue: V)(func: (V, V) => V): RDD[(K, V)] = {
        aggregateByKey(zeroValue)(func, func)
    }
    • 适用场景:当聚合操作需要一个初始值时使用,如从初始值开始累积计算。
  • 示例

    python 复制代码
    rdd = sc.parallelize([("a", 1), ("b", 2), ("a", 3)])
    result = rdd.foldByKey(0, lambda x, y: x + y)
    print(result.collect())

    输出[('a', 4), ('b', 2)]


4. aggregateByKey

  • 功能:支持更复杂的聚合操作,提供了分区内和分区间不同的聚合函数。

  • 源码分析
    aggregateByKey 是最通用的聚合算子,调用了 combineByKeyWithClassTag 方法来控制分区内和分区间的计算方式。

    scala 复制代码
    def aggregateByKey[U: ClassTag](zeroValue: U)(
        seqOp: (U, V) => U,
        combOp: (U, U) => U): RDD[(K, U)] = {
        // Implementation detail here
    }
    • 适用场景:适合复杂的聚合逻辑需求,例如在分区内和分区间使用不同的函数。
  • 示例

    python 复制代码
    rdd = sc.parallelize([("a", 1), ("b", 2), ("a", 3)])
    result = rdd.aggregateByKey(0,
                                lambda x, y: x + y,   # 分区内加和
                                lambda x, y: x + y)   # 分区间加和
    print(result.collect())

    输出[('a', 4), ('b', 2)]


区别总结

  • groupByKey:按键分组返回集合,适合分组场景,但内存消耗大。
  • reduceByKey:按键聚合,没有初始值,适用于聚合计算。
  • foldByKey:按键聚合,支持初始值,适合自定义累加计算。
  • aggregateByKey:最灵活的聚合算子,适合复杂逻辑。
相关推荐
努力的小T2 小时前
使用 Docker 部署 Apache Spark 集群教程
linux·运维·服务器·docker·容器·spark·云计算
Java资深爱好者7 小时前
在Spark中,如何使用DataFrame进行高效的数据处理
大数据·分布式·spark
阿里云大数据AI技术10 小时前
美的楼宇科技基于阿里云 EMR Serverless Spark 构建 LakeHouse 湖仓数据平台
大数据·阿里云·spark·serverless·emr
python资深爱好者13 小时前
什么容错性以及Spark Streaming如何保证容错性
大数据·分布式·spark
猪猪果泡酒15 小时前
spark
spark
weixin_307779131 天前
PySpark实现MERGE INTO的数据合并功能
大数据·python·spark
lucky_syq2 天前
Spark算子:大数据处理的魔法棒
大数据·分布式·spark
D愿你归来仍是少年2 天前
解决Python升级导致PySpark任务异常方案
大数据·开发语言·python·spark
weixin_307779132 天前
PySpark检查两个DataFrame的数据是否一致
大数据·spark·pandas
人类群星闪耀时2 天前
数据湖与数据仓库:初学者的指南
大数据·数据仓库·spark