第五章_Spark核心编程_Rdd_转换算子_Value型_groupBy算子

1. 定义

/*
* 1. 定义
*     //使用 hasPartitioner ; 分区个数使用 父RDD分区个数
*     def groupBy[K](f: T => K)(implicit kt: ClassTag[K]): RDD[(K, Iterable[T])]
*     //使用 HashPartitioner; 指定分区个数
*     def groupBy[K](f: T => K,numPartitions: Int)(implicit kt: ClassTag[K]): RDD[(K, Iterable[T])]
*     //使用自定义分区
*     def groupBy[K](f: T => K, p: Partitioner)(implicit kt: ClassTag[K], ord: Ordering[K] = null): RDD[(K, Iterable[T])]
*
* 2. 功能
*     1. 按照传入的的规则(f: T => K),对元素进行分组,每个组包含一个key,和一个元素序列(K, Iterable[T])
*           ,在根据key进行分区,最终生成 ShuffledRDD
*
* 3. note
*     1. 这个算子操作代价是非常大的(map端不会合并数据,肯能造成内存溢出)
*     2. 如果分组的目的是为了做 聚合操作,那么可以使用 reduceByKey、aggregateByKey 效率会高很多(map端会合并)
* */
/* groupBy 算子 */
object RddTransitionOperator_groupBy extends App {
  private val sc: SparkContext = CommonUtils.getSparkContext("groupBy operator")

  private val rdd: RDD[Long] = sc.range(1, 11, 1, 2)

  //1.使用 hasPartitioner ; 分区个数使用 父RDD分区个数
  private val rdd1: RDD[(Int, Iterable[Long])] = rdd.groupBy(
    e =>
      e % 3 match {
        case 0 => 0
        case 1 => 1
        case 2 => 3
      }
  )

  println(s"rdd类型:${rdd.getClass.getName}")
  println(s"rdd1类型:${rdd1.getClass.getName}")
  println(s"rdd分区个数:${rdd.getNumPartitions}")
  println(s"rdd1分区个数:${rdd1.getNumPartitions}")
  //  rdd类型:org.apache.spark.rdd.MapPartitionsRDD
  //  rdd1类型:org.apache.spark.rdd.ShuffledRDD
  //  rdd分区个数:2
  //  rdd1分区个数:2

  rdd1.collect().foreach(println(_))
  //  (0,CompactBuffer(3, 6, 9))
  //  (1,CompactBuffer(1, 4, 7, 10))
  //  (3,CompactBuffer(2, 5, 8))

  //2.使用 HashPartitioner; 指定分区个数
  private val rdd2: RDD[(Int, Iterable[Long])] = rdd.groupBy(
    (e: Long) => {
      e % 3 match {
        case 0 => 0
        case 1 => 1
        case 2 => 2
      }
    }
    , 3
  )
  println(s"rdd2类型:${rdd2.getClass.getName}")
  println(s"rdd2分区个数:${rdd2.getNumPartitions}")
  //  rdd2类型:org.apache.spark.rdd.ShuffledRDD
  //  rdd2分区个数:3

  rdd2.collect().foreach(println(_))
  //  (0,CompactBuffer(3, 6, 9))
  //  (1,CompactBuffer(1, 4, 7, 10))
  //  (2,CompactBuffer(2, 5, 8))

  //3.使用自定义分区
  private val rdd3: RDD[(Int, Iterable[Long])] = rdd.groupBy(
    (e: Long) => {
      e % 3 match {
        case 0 => 0
        case 1 => 1
        case 2 => 2
      }
    }
    , CustomPartitioner(6)
  )
  println(s"rdd3类型:${rdd3.getClass.getName}")
  println(s"rdd3分区个数:${rdd3.getNumPartitions}")
  //  rdd3类型:org.apache.spark.rdd.ShuffledRDD
  //  rdd3分区个数:6

  rdd3.collect().foreach(println(_))
  //  (0,CompactBuffer(3, 6, 9))
  //  (1,CompactBuffer(1, 4, 7, 10))
  //  (2,CompactBuffer(2, 5, 8))

  rdd3.saveAsTextFile("src/main/data/output/listrdd5")


  sc.stop()

  //while (true) {}

}

/* 自定义分区器 */
case class CustomPartitioner(partitions: Int) extends Partitioner {
  override def numPartitions: Int = partitions

  override def getPartition(key: Any): Int = {
    //根据 1、2、3、4分区
    println(s"分区器中的key:${key.asInstanceOf[Int]}")
    key.asInstanceOf[Int] match {
      case 0 => 0
      case 1 => 1
      case 2 => 2
      case 3 => 3
      case 4 => 4
      case _ => 5
    }
  }
}

 

需求1 : 将 List("Hello", "hive", "hbase", "Hadoop")根据单词首写字母进行分组

  object groupByTest1 extends App {
    val sparkconf: SparkConf = new SparkConf().setMaster("local").setAppName("groupByTest")

    val sc: SparkContext = new SparkContext(sparkconf)

    val rdd = sc.makeRDD(List("Hello", "hive", "hbase", "Hadoop"), 2)

    var rdd1: RDD[(String, Iterable[String])] = rdd.groupBy(
      _.substring(0, 1)
    )

    println(rdd1.collect().mkString(","))

    sc.stop()

    /*
    * 执行结果
    *     (负数,CompactBuffer(-2, -10, -100)),(正数,CompactBuffer(1, 3, 14, 1))
    *
    * */


  }

需求2 : 从服务器日志数据apache.log中获取每个时间段访问量(按小时统计)

  object groupByTest2 extends App {
    val sparkconf: SparkConf = new SparkConf().setMaster("local").setAppName("groupByTest")

    val sc: SparkContext = new SparkContext(sparkconf)

    val rdd = sc.textFile("Spark_319/src/data/input/apache.log")

    //从日志行中 获取小时
    private val rdd1: RDD[String] = rdd.map(_.split(" ")(3).split(":")(1))

    //对小时分组
    private val rdd2: RDD[(String, Iterable[String])] = rdd1.groupBy(
      e => e
    )

    //对小时分组内求和
    private val rdd3: RDD[(String, Int)] = rdd2.map(
      tp => (tp._1, tp._2.size)
    )

    println(rdd3.collect().mkString(","))

    sc.stop()

    /*
    * 执行结果
    *     (14,498),(21,453),(06,366),(20,486),(19,493)...
    *
    * */
  }

需求3 : WordCount

  object groupByTest3 extends App {
    val sparkconf: SparkConf = new SparkConf().setMaster("local").setAppName("groupByTest")

    val sc: SparkContext = new SparkContext(sparkconf)
    //读取文件
    val rdd = sc.textFile("Spark_319/src/data/*.txt")

    //拆分单词
    private val rdd1: RDD[String] = rdd.flatMap(_.split(" "))

    //对单词分组
    private val rdd2: RDD[(String, Iterable[String])] = rdd1.groupBy(e => e)

    //对分组统计
    private val rdd3: RDD[(String, Int)] = rdd2.map(tp => (tp._1, tp._2.size))

    rdd3.collect().foreach(println(_))

    sc.stop()

    /*
    * 执行结果
    *     (曹操,6)
          (关羽,3)
          (张飞,3)
          (孙权,3)
          (刘备,3)
          (政党,3)
          (政府,3)
    *
    * */


  }

 

posted @ 2022-03-23 12:15  学而不思则罔!  阅读(133)  评论(0)    收藏  举报