SparkCore-RDD编程操作03

1. Spark程序执行过程

1.1. WordCount案例程序的执行过程

在这里插入图片描述

1.2. Spark程序执行流程

在这里插入图片描述

2. RDD的操作

2.1. RDD的初始化

​ RDD的初始化,原生api提供的2中创建方式,一种就是读取文件textFile,还有一种就是加载一个scala集合parallelize。当然,也可以通过transformation算子来创建的RDD。

2.2. RDD的操作

需要知道RDD操作算子的分类,基本上分为两类:transformation和action,当然更加细致的分,可以分为输入算子,转换算子,缓存算子,行动算子。

在这里插入图片描述

输入:在Spark程序运行中,数据从外部数据空间(如分布式存储:textFile读取HDFS等,parallelize方法输入Scala集合或数据)输入Spark,数据进入Spark运行时数据空间,转化为Spark中的数据块,通过BlockManager进行管理。

运行:在Spark数据输入形成RDD后便可以通过变换算子,如filter等,对数据进行操作并将RDD转化为新的RDD,通过Action算子,触发Spark提交作业。

缓存:如果数据需要复用,可以通过Cache算子,将数据缓存到内存。

输出:程序运行结束数据会输出Spark运行时空间,存储到分布式存储中(如saveAsTextFile输出到HDFS),或Scala数据或集合中(collect输出到Scala集合,count返回Scala int型数据)。

SparkCore编程

 val conf = new SparkConf()
      .setAppName("Demo2")//类名
      .setMaster("local[*]")//指定线程数
    val sc = new SparkContext(conf)
    sc加载数据生成rdd
    rdd使用算子
    sc.stop()

2.3. transformation转换算子详细案例大全

2.4. action行动算子使用案例大全

2.5. 持久化操作

2.6. 共享变量(广播变量/累加器)

3. 高级排序

3.1. 普通的排序

object Demo1 {
  def main(args: Array[String]): Unit = {
    val conf = new SparkConf()
      .setAppName("_01SparkSortOps")
      .setMaster("local[2]")//在此处和parallelize处都能设置数据分区,此处设置作用于全局
    val sc = new SparkContext(conf)

    val stus:RDD[Student] = sc.parallelize(List(
      Student("卫超", 19, 187),
      Student("王礼鹏", 29, 177),
      Student("乌欣欣", 18, 168),
      Student("陈延年", 19, 157),
      Student("刘龙沛", 20, 175)
    ))
    //都按降序
    //sortbykey(stus)
    //sortby(stus)
    takeOrdered(stus)
  }

  /*排序的算子主要有:
  *    transformation:
    *          sortByKey
      *              按照key进行排序,默认升序,对应的数据类型是K-V
  *              ascending         升序(true)或者降序(false)
  *              numPartitions     排序之后的分区个数
  *          sortBy
    *              可以不用有key来进行排序,但是需要指定进行排序的字段,底层其实就是
  *              map(data => (K, data)).sortBykey()
  *          区别就是groupByKey和groupBy,二者的排序操作,默认是分区内局部有序,全局不一定,如果要做到全局有序,那么partition个数就设置为1,
  *          但是此时的计算性能非常差
    *    action:
    *      takeOrdered: 在拉取数据集的同时,对数据进行排序,如果一些操作,需要进行排序,并返回结果值,此时可以使用takeOrdered而不是先sortBy在take
  *
  */
  //sortbykey
  def sortbykey(stus:RDD[Student]): Unit ={
    stus.map(stu=>(stu.height,stu)).sortByKey(false,1).mapPartitionsWithIndex((index,partition)=>{
      val list = partition.toList
      println(s"分区:${index}的数据为:${list.mkString(",")}")
      list.toIterator
    }).count()
  }
  //sortby
  def sortby(stus:RDD[Student]): Unit ={
    stus.sortBy(stu=>stu.height,false,1).mapPartitionsWithIndex((index,partition)=>{
      val list = partition.toList
      println(s"分区:${index}的数据为:${list.mkString(",")}")
      list.toIterator
    }).count()
  }
  //takeOrdered
  def takeOrdered(stus:RDD[Student]): Unit ={
    val students = stus.takeOrdered(3)(new Ordering[Student]() {
      override def compare(x: Student, y: Student): Int = {
        y.height.compareTo(x.height)
      }
    })
    students.foreach(println)
  }
}
case class Student(name:String,age:Int,height:Int)

3.2. 二次排序

/**
 * @author ljh
 * @create 2020-12-09 11:26
 *二次排序
 */
object Demo2 {
  def main(args: Array[String]): Unit = {
    val conf = new SparkConf()
      .setAppName("_01SparkSortOps")
      .setMaster("local[2]") //在此处和parallelize处都能设置数据分区,此处设置作用于全局
    val sc = new SparkContext(conf)

    val stus: RDD[Student] = sc.parallelize(List(
      Student("卫超", 19, 187),
      Student("王礼鹏", 29, 177),
      Student("乌欣欣", 18, 168),
      Student("陈延年", 19, 157),
      Student("刘龙沛", 20, 177)
    ))
    //sortbykey(stus)
    //sortby(stus)
    takeOrdered(stus)
  }
  //takeOrdered不用改,就可以二次排序
  def takeOrdered(stus:RDD[Student]): Unit ={
    val students = stus.takeOrdered(3)(new Ordering[Student]() {
      override def compare(x: Student, y: Student): Int = {
        var i = x.height.compareTo(y.height)
        if (i == 0) {
          i = y.age.compareTo(x.age)
        }
        i
      }
    })
    students.foreach(println)
  }
  //按照身高升序,如果身高相同,按照年龄进行升序
  def sortbykey(stus:RDD[Student]): Unit ={
    val value = stus.map(stu => (HeightAgeKey(stu.height, stu.age), stu)).sortByKey(numPartitions = 1)
    value.foreach(println)
  }
  //按照身高升序,如果身高相同,按照年龄进行降序
  def sortby(stus:RDD[Student]): Unit = {
    stus.sortBy(stu => stu, numPartitions = 1)(new Ordering[Student] {
      override def compare(x: Student, y: Student): Int = {
        var i = x.height.compareTo(y.height)
        if (i == 0) {
          i = y.age.compareTo(x.age)
        }
        i
      }
    },ClassTag.Object.asInstanceOf[ClassTag[Student]]//运行时的泛型对象
    ).foreach(println)

  }
}

case class HeightAgeKey(height:Int,age:Int) extends Ordered[HeightAgeKey] {
  override def compare(that: HeightAgeKey): Int = {
    var i = height.compareTo(that.height)
    if(i==0){
      i=age.compareTo(that.age)
    }
    i
  }
}

3.3. 分组排序

object Demo3 {
  def main(args: Array[String]): Unit = {
    val conf = new SparkConf()
      .setAppName("Demo3")
      .setMaster("local[2]")
    val sc = new SparkContext(conf)

    val lines = sc.parallelize(List(
      "chinese ls 91",
      "english ww 56",
      "chinese zs 90",
      "chinese zl 76",
      "english zq 88",
      "chinese wb 95",
      "chinese sj 74",
      "english ts 87",
      "english ys 67",
      "english mz 77",
      "chinese yj 98",
      "english gk 96"
    ))
    //按course分组
    val courseAndScore = lines.map(line => {
      val arr = line.split("\\s+")
      var course = arr(0)
      var score = arr(1) + "|" + arr(2)
      (course, score)
    }).groupByKey(numPartitions = 1)
    //分组排序前
    courseAndScore.foreach{case(course,score)=>{
      println(s"${course}--${score.mkString(",")}")
    }}
    //分组排序
    //map每次进入一个分组,对一组中的score迭代器操作
    //先将score迭代器转成list
    val courseScoreSort = courseAndScore.map { case (course, score) => {
      val listScore = score.toList
      //对list排序,取list中每个元素s1,s2的成绩排序
      val scoreSort = listScore.sortWith((s1, s2) => {
        val score1 = s1.split("\\|")(1)
        val score2 = s2.split("\\|")(1)
        score1 > score2//降序
      })
      //返回排序结果
      (course, scoreSort)
    }
    }
    //分组排序后
    courseScoreSort.foreach{case(course,score)=>{
      println(s"${course}--${score.mkString(",")}")
    }}
  }

}

3.4.分组排序求topN优化

sortWith排序,combineByKey优化

object Demo4 {
  def main(args: Array[String]): Unit = {
    val conf = new SparkConf()
      .setAppName("Demo3")
      .setMaster("local[2]")
    val sc = new SparkContext(conf)

    val lines = sc.parallelize(List(
      "chinese ls 91",
      "english ww 56",
      "chinese zs 90",
      "chinese zl 76",
      "english zq 88",
      "chinese wb 95",
      "chinese sj 74",
      "english ts 87",
      "english ys 67",
      "english mz 77",
      "chinese yj 98",
      "english gk 96"
    ))
    //按course分组---优化,使用combinebykey预聚合
    val courseAndScore = lines.map(line => {
      val arr = line.split("\\s+")
      var course = arr(0)
      var score = arr(1) + "|" + arr(2)
      (course, score)
    }).combineByKey(createCombiner,mergeValue,mergeCombiners)

    //分组排序前
    courseAndScore.foreach{case(course,score)=>{
      println(s"${course}--${score.mkString(",")}")
    }}
    //分组排序
    //map每次进入一个分组,对一组中的score迭代器操作
    //先将score迭代器转成list
    val courseScoreSort = courseAndScore.map { case (course, score) => {
      val listScore = score.toList
      //对list排序,取list中每个元素s1,s2的成绩排序
      val scoreSort = listScore.sortWith((s1, s2) => {
        val score1 = s1.split("\\|")(1)
        val score2 = s2.split("\\|")(1)
        score1 > score2//降序
      }).take(3)
      //返回排序结果
      (course, scoreSort)
    }
    }
    //分组排序后
    courseScoreSort.foreach{case(course,score)=>{
      println(s"${course}--${score.mkString(",")}")
    }}
  }
  def createCombiner(str:String): Array[String] ={
    Array(str)
  }
  def mergeValue(arr:Array[String],str:String):Array[String]={
    arr.:+(str)
  }
  def mergeCombiners(arr:Array[String],arr1:Array[String]):Array[String]={
    arr.++(arr1)
  }
}

3.5. 分组排序求topN最终

最终优化,combineByKey分组,treeset求topN

object Demo5 {
  def main(args: Array[String]): Unit = {
    val conf = new SparkConf()
      .setAppName("Demo5")
      .setMaster("local[2]")
    val sc = new SparkContext(conf)

    val lines = sc.parallelize(List(
      "chinese ls 91",
      "english ww 56",
      "chinese zs 90",
      "chinese zl 76",
      "english zq 88",
      "chinese wb 95",
      "chinese sj 74",
      "english ts 87",
      "english ys 67",
      "english mz 77",
      "chinese yj 98",
      "english gk 96"
    ))
    //按course分组---优化,使用combinebykey预聚合
    val courseAndScore = lines.map(line => {
      val arr = line.split("\\s+")
      var course = arr(0)
      var score = arr(1) + "|" + arr(2)
      (course, score)
    }).combineByKey(createCombiner,mergeValue,mergeCombiners)

    //显示排序结果
    courseAndScore.foreach{case(course,score)=>{
      println(s"${course}--${score.mkString(",")}")
    }}
    sc.stop()
  }

  def createCombiner(str:String): mutable.TreeSet[String]={
    mutable.TreeSet(str)(new Ordering[String] {
      override def compare(x: String, y: String): Int = {
        val s1 = x.split("\\|")(1)
        val s2 = y.split("\\|")(1)
        s1.compareTo(s2)
      }
    })
  }
  def mergeValue(tree:mutable.TreeSet[String],str:String):mutable.TreeSet[String]={
    //先add,treeset会自动排序,然后删除最后面的
    tree.add(str)
    if(tree.size>3){
      tree.dropRight(1)//此方法是有返回值的,生成一个新的set,所以要加上else,避免返回原set
    }else{
      tree
    }
  }
  def mergeCombiners(tree:mutable.TreeSet[String],tree1:mutable.TreeSet[String]):mutable.TreeSet[String]={
    for(a<-tree1){
      tree.add(a)
    }
    if(tree.size>3){
      tree.dropRight(tree.size-3)
    }else{
      tree
    }

  }
}

3.6. 分区

/**
 * Spark rdd操作分区
 * 涉及到分区的操作,主要集中在这么几处:
 * 1、就是在输入操作
 *
 * 2、shuffle操作的时候会有分区的操作
 *
 * spark提供的分区方式,默认有2种:
 *  HashPartitioner(默认的)
 *  RangePartitioner(输入算子通常所使用)
 *
 * 自定义分区操作
 *
 *  需求:按照每一个科目一个文件,将数据进行重新的划分
 *      显然这就得需要用到重分区操作,但是该重分区不是repartition或者coleasce
 *  在spark rdd的操作中有一个算子专门可以进行分区--partitionBy
 *    当然,除了该算子以外,其他的只要是shuffle操作,通常都会有第二个参数,叫partitioner,也可以通过该参数来完成分区
 *
 */
object Demo1 {
  def main(args: Array[String]): Unit = {
    val conf = new SparkConf()
      .setAppName("Demo1")
      .setMaster("local[2]")
    val sc = new SparkContext(conf)

    val lines = sc.parallelize(List(
      "chinese ls 91",
      "english ww 56",
      "chinese zs 90",
      "math zl 76",
      "english zq 88",
      "chinese wb 95",
      "chinese sj 74",
      "english ts 87",
      "math ys 67",
      "english mz 77",
      "chinese yj 98",
      "english gk 96",
      "math zq 88",
      "chinese wb 95",
      "math sj 74",
      "english ts 87",
      "math ys 67",
      "english mz 77",
      "math yj 98",
      "english gk 96"
    ))
    val map = lines.map(line => {
      val i = line.indexOf(" ")
      val course = line.substring(0, i)
      val score = line.substring(i + 1)
      (course, score)
    })
    val array = map.keys.distinct().collect()
    map.partitionBy(new Mypartition(array)).saveAsTextFile("C:\\Users\\70201\\Desktop\\test\\partition")




    sc.stop()
  }


}
class Mypartition(keys:Array[String]) extends Partitioner {
  val key2PartitionId = {

    val map = mutable.Map[String, Int]()
    for(i <- 0 until keys.length) {
      map.put(keys(i), i)
    }
    map
  }
  //最后的分区的个数
  override def numPartitions: Int = keys.length
  //将对应的key映射到相匹配的partitionId
  override def getPartition(key: Any): Int = {
    key2PartitionId.getOrElse(key.toString, 0)
  }
}

4.spark总结

在这里插入图片描述

在这里插入图片描述

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值