Spark RDD 转换算子总结

427 阅读5分钟

map 和 mapPartitions 的区别

  1. 数据处理角度

    Map 算子是分区内一个数据一个数据的执行,类似于串行操作。

    MapPartitions 算子是以分区为单位进行批处理操作。

  2. 功能角度

    Map 算子因为类似于串行操作,所以性能比较低。

    MapPartitions 算子类似于批处理,所以性能较高。

    但是 mapPartitions 算子会长时间占用内存,那么这样会导致内存可能不够用,出现内存溢出的错误。所以在内存有限的情况下,不推荐使用,推荐使用 map 操作。

groupBy

  1. 将数据根据指定的规则进行分组, 分区默认不变,但是数据会被打乱重新组合,我们将这样的操作称之为 shuffle。
  2. 极限情况下,数据可能被分在同一个分区中,一个组的数据在一个分区中,但是并不是说一个分区中只有一个组。

filter

  1. 将数据根据指定的规则进行筛选过滤,符合规则的数据保留,不符合规则的数据丢弃。
  2. 当数据进行筛选过滤后,分区不变,但是分区内的数据可能不均衡,生产环境下,可能会出现数据倾斜。

sample

根据指定的规则从数据集中抽取数据:

  1. 抽取数据不放回(伯努利算法)
伯努利算法:又叫 01 分布。例如扔硬币,要么正面,要么反面。
具体实现:根据种子和随机算法算出一个数和第二个参数设置几率比较,小于第二个参数要,大于不要

第一个参数:抽取的数据是否放回,false:不放回

第二个参数:抽取的几率,范围在[0,1]之间,0:全不取;1:全取;

第三个参数:随机数种子

2.抽取数据放回(泊松算法)

第一个参数:抽取的数据是否放回,true:放回;false:不放回

第二个参数:重复数据的几率,范围大于等于 0.表示每一个元素被期望抽取到的次数

第三个参数:随机数种子

coalesce

  1. 根据数据量缩减分区,用于大数据集过滤后,提高小数据集的执行效率。
  2. 当 spark 程序中,存在过多的小任务的时候,可以通过 coalesce 方法,收缩合并分区,减少分区的个数,减小任务调度成本。

repartition

  1. 该操作内部其实执行的是 coalesce 操作,参数 shuffle 的默认值为 true。
  2. 无论是将分区数多的RDD 转换为分区数少的 RDD,还是将分区数少的 RDD 转换为分区数多的 RDD,repartition操作都可以完成,因为无论如何都会经 shuffle 过程。

intersection(交集)、union(并集)、subtract(差集)

两个RDD数据类型必须一致

zip(拉链)

  1. 两个RDD数据类型可以不一致
  2. 两个RDD数据分区必须一致
java.lang.IllegalArgumentException: Can't zip RDDs with unequal numbers of partitions
  1. 两个RDD分区数据数量必须一致
org.apache.spark.SparkException: Can only zip RDDs with same number of elements in each partition

partitionBy

将数据按照指定 Partitioner 重新进行分区。Spark 默认的分区器是 HashPartitioner。

  1. 如果重分区的分区器和当前RDD的分区器一致,底层进行partitioner的equals的判断,判断分区器是否相同以及分区数是否相同,如果两者都相等则返回当前,不做任何处理。

  2. spark可用分区器:

    • 哈希分区器(Hash Partitioner)
    • 范围分区器(Range Partitioner)
  3. 自定义分区器:

    要实现自定义分区,需要继承Partitioner类,并实现以下方法:

    • numPartitions:返回分区数
    • getPartition:返回执行key的分区ID
    • equals:判断相等性的标准方法,Spark通过该方法判断RDD间的分区器示例是否相同,由此决定是否进行shuffle操作。

reduceByKey 和 groupByKey 区别

  1. 从shuffle角度:

    reduceByKey 和 groupByKey 都存在 shuffle 的操作,但是 reduceByKey可以在 shuffle 前对分区内相同 key 的数据进行预聚合(combine)功能,这样会减少落盘的数据量,而 groupByKey 只是进行分组,不存在数据量减少的问题,reduceByKey 性能比较高。

  2. 从功能角度:

    reduceByKey 其实包含分组和聚合的功能。GroupByKey 只能分组,不能聚合,所以在分组聚合的场合下,推荐使用 reduceByKey,如果仅仅是分组而不需要聚合,只能使用 groupByKey。

reduceByKey、foldByKey、aggregateByKey、combineByKey 的区别

  1. ReduceByKey: 相同 key 的第一个数据不进行任何计算,分区内和分区间计算规则相同。
  2. FoldByKey: 相同 key 的第一个数据和初始值进行分区内计算,分区内和分区间计算规则相同。
  3. AggregateByKey:相同 key 的第一个数据和初始值进行分区内计算,分区内和分区间计算规则可以不相同。
  4. CombineByKey:当计算时,发现数据结构不满足要求时,可以让第一个数据转换结构。分区内和分区间计算规则不相同。
        reduceByKey:

             combineByKeyWithClassTag[V](
                 (v: V) => v, // 第一个值不会参与计算
                 func, // 分区内计算规则
                 func, // 分区间计算规则
                 )

        aggregateByKey :

            combineByKeyWithClassTag[U](
                (v: V) => cleanedSeqOp(createZero(), v), // 初始值和第一个key的value值进行的分区内数据操作
                cleanedSeqOp, // 分区内计算规则
                combOp,       // 分区间计算规则
                )

        foldByKey:

            combineByKeyWithClassTag[V](
                (v: V) => cleanedFunc(createZero(), v), // 初始值和第一个key的value值进行的分区内数据操作
                cleanedFunc,  // 分区内计算规则
                cleanedFunc,  // 分区间计算规则
                )

        combineByKey :

            combineByKeyWithClassTag(
                createCombiner,  // 相同key的第一条数据进行的处理函数
                mergeValue,      // 表示分区内数据的处理函数
                mergeCombiners,  // 表示分区间数据的处理函数
                )

join

只处理key相等的数据。