04-Spark核心编程-RDD-02

134 阅读5分钟

四、基础编程

  • 双Value算子
    1. intersection
      求两个RDD的交集
      函数签名:

      def intersection(other: RDD[T]): RDD[T]
      

      示例:

      val dataRDD1 = sc.makeRDD(List(1,2,3,4))
      val dataRDD2 = sc.makeRDD(List(3,4,5,6))
      val dataRDD = dataRDD1.intersection(dataRDD2)
      

      注意:若两个RDD的数据类型不一致,则无法做交集运算。

    2. union
      求两个RDD的并集
      函数签名:

      def union(other: RDD[T]): RDD[T]
      

      示例:

      val dataRDD1 = sparkContext.makeRDD(List(1,2,3,4))
      val dataRDD2 = sparkContext.makeRDD(List(3,4,5,6))
      val dataRDD = dataRDD1.union(dataRDD2)
      

      注意:若两个RDD的数据类型不一致,则无法做并集运算。

    3. subtract
      以一个RDD元素为主,去除两个RDD中重复元素,将其他元素保留下来。求差集。
      函数签名:

      def subtract(other: RDD[T]): RDD[T]
      

      示例:

      val dataRDD1 = sc.makeRDD(List(1,2,3,4))
      val dataRDD2 = sc.makeRDD(List(3,4,5,6))
      val dataRDD = dataRDD1.subtract(dataRDD2)
      

      注意:若两个RDD的数据类型不一致,则无法做差集运算。

    4. zip
      将两个RDD中的元素,以键值对的形式进行合并。其中,键值对中的Key为第1个RDD中的元素,Value为第2个RDD中的元素。
      函数签名:

      def zip[U: ClassTag](other: RDD[U]): RDD[(T, U)]
      

      示例:

      val dataRDD1 = sc.makeRDD(List(1,2,3,4))
      val dataRDD2 = sc.makeRDD(List(3,4,5,6))
      val dataRDD = dataRDD1.zip(dataRDD2)
      

      注意:
      1. 若两个RDD的数据类型不一致,则无法做拉链运算。
      2. 若两个RDD的数据量不一致,则无法做拉链运算。
      3. 若两个RDD的分区数不一致,则无法做拉链运算。

  • KV算子
    顾名思义,KV类型的算子需要对 K-V 键值对类型的数据进行处理,因此在大多数情况下,会配合 map 算子使用。
    1. partitionBy
      将数据按照指定Partitioner重新进行分区。Spark默认的分区器是HashPartitioner。

      函数签名:

      def partitionBy(partitioner: Partitioner): RDD[(K, V)]
      

      示例:

      val rdd: RDD[Int] = sc.makeRDD(List(1,2,3,4), 2)
      val rdd1: RDD[(Int, Int)] = rdd.map((_, 1))
      val rdd2: RDD[(Int, String)] = rdd.partitionBy(new HashPartitioner(2))
      

      Spark提供了三个分区器:HashPartitioner、RangePartitioner 以及 PythonPartitioner。由于 PythonPartitioner 存在限制,且受同包约束,并不通用,这里不再介绍。

      RangePartitioner,范围分区器。顾名思义,可以按照一定范围分区,例如1-5分区,6-10分区。但该分区器需要数据可以进行比较,因此使用也较少。一般在排序时使用。

      Spark也提供了自定义分区器功能:

      val wordRDD: RDD[String] = sc.makeRDD(List("Hello", "World", "Hello", "Spark"))
      
      wordRDD.map((_, 1)).partitionBy(new myPartition()).cache().collect().foreach(println)
      class myPartition extends Partitioner{
        // 分区数量
        override def numPartitions: Int = 3
      
        // 分区规则
        override def getPartition(key: Any): Int = {
          key match {
            case "Hello" => 1
            case "World" => 2
            case _ => 3
          }
        }
      }
      

      注意:
      1. 与repartition算子相比,partitionBy更侧重对数据的分区。例如奇数偶数、正数负数之类的分区,repartition更侧重分区数量的变化。
      2. 二者都存在shuffle过程。

    2. reduceByKey
      可以将数据按照相同的Key对Value进行聚合

      函数签名:

      def reduceByKey(func: (V, V) => V): RDD[(K, V)]
      def reduceByKey(func: (V, V) => V, numPartitions: Int): RDD[(K, V)]
      

      示例:

      val dataRDD1 = sc.makeRDD(List(("a",1),("b",2),("c",3)))
      val dataRDD2 = dataRDD1.reduceByKey(_+_)
      val dataRDD3 = dataRDD1.reduceByKey(_+_, 2)
      
    3. groupByKey
      将分区的数据直接转换为相同类型的内存数组.

      函数签名:

      def reduceByKey(func: (V, V) => V): RDD[(K, V)]
      def reduceByKey(func: (V, V) => V, numPartitions: Int): RDD[(K, V)]
      

      示例:

      val dataRDD1 = sc.makeRDD(List(("a",1),("b",2),("a",3)))
      val dataRDD2 = dataRDD1.groupByKey()
      val dataRDD3 = dataRDD1.groupByKey(2)
      val dataRDD4 = dataRDD1.groupByKey(new HashPartitioner(2))
      

      注意:
      reduceByKey和groupByKey都是对数据进行聚合,区别在于:
      1. groupByKey只是单纯将数据进行分组,结果是<k, value-list>形式;而reduceByKey则多了一步计算的操作;
      2.从效率上来看,reduceByKey要比groupByKey的效率要好,因为reduceByKey底层提供了预聚合的操作。

    4. aggregateByKey
      将数据根据不同的规则进行分区内计算和分区间计算。
      函数签名:

      def aggregateByKey[U: ClassTag](zeroValue: U)(seqOp: (U, V) => U, combOp: (U, U) => U): RDD[(K, U)]
      

      示例:

      // 取出每个分区内相同key的最大值然后分区间相加
      val rdd = sc.makeRDD(List(("a",1),("a",2),("c",3),("b",4),("c",5),("c",6)),2)
      // 0:("a",1),("a",2),("c",3) => (a,10)(c,10) => 和下面合并
      // 1:("b",4),("c",5),("c",6) => (b,10)(c,10) => (a,10)(b,10)(c,20)
      val resultRDD = rdd.aggregateByKey(10)(
          (x, y) => math.max(x,y),// 分区内做运算
          (x, y) => x + y // 分区间做运算
      )
      resultRDD.collect().foreach(println)
      
    5. foldByKey
      当分区内计算规则和分区间计算规则相同时,aggregateByKey就可以简化为foldByKey。 函数签名:

      def foldByKey(zeroValue: V)(func: (V, V) => V): RDD[(K, V)]
      

      示例:

      val dataRDD1 = sc.makeRDD(List(("a",1),("b",2),("c",3)))
      val dataRDD2 = dataRDD1.foldByKey(0)(_+_)
      
    6. combineByKey
      最通用的对key-value型rdd进行聚集操作的聚集函数(aggregation function)。类似于aggregate(),combineByKey()允许用户返回值的类型与输入不一致。

      示例:

      // 将数据List(("a", 88), ("b", 95), ("a", 91), ("b", 93), ("a", 95), ("b", 98))求每个key的平均值
      val list: List[(String, Int)] = List(("a", 88), ("b", 95), ("a", 91), ("b", 93), ("a", 95), ("b", 98))
      val input: RDD[(String, Int)] = sc.makeRDD(list, 2)
      val combineRdd: RDD[(String, (Int, Int))] = input.combineByKey(
          (_, 1),
          (acc: (Int, Int), v) => (acc._1 + v, acc._2 + 1),
          (acc1: (Int, Int), acc2: (Int, Int)) => (acc1._1 + acc2._1, acc1._2 + acc2._2)
      )
      

      几种ByKey算子的区别

    7. sortByKey
      在一个(K,V)的RDD上调用,K必须实现Ordered接口,返回一个按照key进行排序的。
      函数签名:

      def sortByKey(ascending: Boolean = true, numPartitions: Int = self.partitions.length): RDD[(K, V)]
      

      示例:

      val dataRDD1 = sc.makeRDD(List(("a",1),("b",2),("c",3)))
      val sortRDD1: RDD[(String, Int)] = dataRDD1.sortByKey(true)
      val sortRDD1: RDD[(String, Int)] = dataRDD1.sortByKey(false)
      
    8. join
      在类型为(K,V)和(K,W)的RDD上调用,返回一个相同key对应的所有元素连接在一起的(K,(V,W))的RDD。
      函数签名:

      def join[W](other: RDD[(K, W)]): RDD[(K, (V, W))]
      

      示例:

      val rdd: RDD[(Int, String)] = sc.makeRDD(Array((1, "a"), (2, "b"), (3, "c")))
      val rdd1: RDD[(Int, Int)] = sc.makeRDD(Array((1, 4), (2, 5), (3, 6)))
      rdd.join(rdd1).collect().foreach(println)
      
    9. cogroup
      在类型为(K,V)和(K,W)的RDD上调用,返回一个相同key对应的所有元素连接在一起的(K,(V,W))的RDD。 函数签名:

      def cogroup[W](other: RDD[(K, W)]): RDD[(K, (Iterable[V], Iterable[W]))]
      

      示例:

      val dataRDD1 = sc.makeRDD(List(("a",1),("a",2),("c",3)))
      val dataRDD2 = sc.makeRDD(List(("a",1),("c",2),("c",3)))
      
      val value: RDD[(String, (Iterable[Int], Iterable[Int]))] = dataRDD1.cogroup(dataRDD2)