package com.shujia.spark.core

import org.apache.spark.rdd.RDD
import org.apache.spark.{Partitioner, SparkConf, SparkContext}

object Demo13Patition {

  def main(args: Array[String]): Unit = {

    val conf: SparkConf = new SparkConf()
      .setMaster("local")
      .setAppName("partition")
      .set("spark.default.parallelism", "23") //shuffle 之后默认并行度

    val sc = new SparkContext(conf)

    /**
      * 分区生产规则
      * 1、默认一个block对应一个分区, 一个task处理128M的数据
      * 2、可以设置最小分区数,实际分区数会根据文件数量进行计算,保证文件能被分开
      * 3、如果block的数量比最小分区数大,以block数量为准
      */

    val linesRDD: RDD[String] = sc.textFile("data/words", 2)

    println("linesRDD分区数据:" + linesRDD.getNumPartitions)

    //没有shuffle算子生成的rdd分区数等于上一个rdd的分区数
    val wordsRDD: RDD[String] = linesRDD.flatMap(_.split(","))

    println("wordsRDD分区数:" + wordsRDD.getNumPartitions)

    /**
      * shuufle 之后rdd分区数
      * 1、如果不指定默认等于前一个rdd分区数
      * 2、可以手动执行分区数 (numPartitions)
      * 3、设置默认并行度spark.default.parallelism
      *
      * 优先级
      * 手动指定---> spark.default.parallelism ---> 前一个rdd分区数
      *
      *
      * 分区数越多--> task 越多---> 计算并行度越高---> 任务越快( 导致产生很多小文件,浪费计算资源)
      */

    val groupByRDD: RDD[(String, Iterable[String])] = wordsRDD.groupBy((w: String) => w, 100)

    println("groupBy分区数:" + groupByRDD.getNumPartitions)

    //groupByRDD.foreach(println)


    val myParttionRDD: RDD[(String, Iterable[String])] = wordsRDD.groupBy((w: String) => w, new MyPartition)


    println("myParttionRDD分区数:" + myParttionRDD.getNumPartitions)


    /**
      * repartition: 没有实际的业务逻辑,只是修改rdd分区数据,但是会产生shuffle
      * repartition : 既可以提高分区也可以减少分区
      *
      * coalesce: 修改分区数据,如果不产生shuufle ,不能用于提高分区数据
      *
      * coalesce(shuffle=false): 一般用于合并小文件,不产生shuffle ,效率高
      *
      */

    val rePartitionRDD: RDD[(String, Iterable[String])] = myParttionRDD.repartition(1000)


    println("rePartitionRDD分区数据:" + rePartitionRDD.getNumPartitions)


    val coalesceRDD: RDD[(String, Iterable[String])] = rePartitionRDD.coalesce(10, false)

    println("coalesceRDD分区数:" + coalesceRDD.getNumPartitions)

    /*
        while (true) {
        }*/

  }

}

/**
  * 自定义分区,默认是hash分区
  *
  */
class MyPartition extends Partitioner {

  //指定rdd分区数
  override def numPartitions: Int = 100

  /**
    * spark 在shuffle的时候会调用这个方法来获取分区数
    *
    */
  override def getPartition(key: Any): Int = {
    ///hash 分区
    math.abs(key.hashCode()) % numPartitions
  }
}

 自定义分区

package com.shujia.spark.core

import org.apache.spark.rdd.RDD
import org.apache.spark.{Partitioner, SparkConf, SparkContext}

object Demo14StudentPatition {
  def main(args: Array[String]): Unit = {
    val conf: SparkConf = new SparkConf()
      .setMaster("local")
      .setAppName("partition")

    val sc = new SparkContext(conf)

    val student: RDD[String] = sc.textFile("data/students.txt", 2)

    println("student的分区数:" + student.getNumPartitions)

    /**
      *
      * 将文科和理科分别保存到不同的文件中
      * 就要分到不同的reduce中去
      * 一个reduce会生成一个文件
      *
      *
      */
      
    val clazzPationRDD: RDD[(String, Iterable[String])] = student.groupBy((stu: String) => stu,new ClassPartition)

    println("clazzPationRDD的分区数:"+clazzPationRDD.getNumPartitions)


    clazzPationRDD.map(_._1).saveAsTextFile("data/clazz")

  }
}

class  ClassPartition extends Partitioner{
  override def numPartitions: Int = 2

  override def getPartition(key: Any): Int = {
    val clazz: String = key.toString.split(",")(4)
    if (clazz.startsWith("文科")){
      0
    }else{
      1
    }


  }
}