算子整体回顾和案例分析

底层的RDD常用的就4个
MapPartitionsRDD:对;每个分区进行操作是一个比较底层的算子具体怎么实现要看底层的computer方法,对RDD进行map本质上是对每一个分区进行map,每一个分区对应一个迭代器,其实是把map方法作用在迭代器上,然后把要传的函数先检查一下看有没有未被序列化的问题然后再传进去,接就是说以后生成task,task如果是一个很复杂的功能他再一个stage里会生成一个迭代器链,而迭代器链里的某一个操作就是对应RDD里面的computer方法
suffleRDD:groupByKey,reudceByKey,aggregateByKey,foldByKey,combinByKey他们底层调用的都是suffleRDD:new出来父RDD包起来,要设置aggregate里面传三个函数,还可以设置MapSideCombine等于true或false
CogroupRDD:他跟groupBy,groupByKey有什么区别他们只能将一个RDD里一个分区或多个分区key相同的进行分组本质上是suffle通过网络把key相同的搞到一起就是下游的task到上游去拉去数据,将同一个分区的啦过来但是key相同的一定会进到同一个分区里面一个分啊u里面会有多个不同的key,分组的话每个可以会在独立的分组里面…而Cougroup会将多个分区进行协同分组就是把多个RDDkey相同的通过网络传输到一个分区的一个组里面本质上也要suffle
subtractedRDD:差集
DAG:是一个有向无环图直白点就是一连串RDD,是对多个RDD的转换过程和依赖关系的描述,触发action就会形成一个完整的DAG,一个DAG就是一个job
Application:使用SparkSubmit向集群提交的一个计算应用,一个Application会有一个drive,但是会有一到多个Excuter,每提交一个Application就会有对应的Ddive和Excuter
Drive把写的逻辑承包给Application,他再承包给SparkSubmit,他再分包给下面的几个Worker
Job:Drive向Executer提交的作业,触发一次action就会形成一个完整的DAG,一个DAG对应一个JOb,一个Job中有以到多个Stage,一个Stage对应一个TaskSet,一个TaskSet中有一到多个Task
Stage:任务执行阶段,Stage执行是由先后顺序的,先执行前面的,后执行后面的,一个stage对应一个TaskSet,一个TaskSet中的Task的数量取决于Stage中最后一个RDD分区的数量,Stage分两种,1ShuffleMapStage,里面对应的Task叫ShuffleMapTask,2ResultStage里面对应的Task叫ResultTask
dependency:依赖关系,值的是父RDD和子RDD之间的依赖关系,窄依赖:没有shfuffle产生,多个算子会被合并到一个Task中,即在一个pipeline中.宽依赖:有shuffle产生,是划分Stage的依据
TaskSet:保存同一种计算逻辑多个Task的集合,一个TaskSet中的Task计算逻辑都一样,计算的数据不一样,
Task:Task分两种.1.ShuffleMapTask可以读取各种数据源的读数据,也可以读取shuffle后的数据专门为shuffle做准备,一定会有shufflewrite.2.ResultTask可以读取各种数据源的读数据,也可以读取shuffle后的数据,专门为产生计算结果.Task其实就是类的实例,有属性,从哪里读数据,有方法如何计算.Task的数量决定了并行度,同时也要考虑可以用的cores

求每门学科最受欢迎的两个老师

数据长这样
http://bigdata.51doit.cn/laozhang

自定义获取sc

import org.apache.spark.{SparkConf, SparkContext}

object SparkUtils {
  def createContext(isLocal: Boolean =true): SparkContext = {
    val conf = new SparkConf().setAppName(this.getClass.getSimpleName)
    if (isLocal){
      conf.setMaster("local[*]")
    }
    val sc =new SparkContext(conf)
    sc
  }

}

方法一

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

import scala.reflect.internal.util.NoSourceFile.lines

object FavTeacher01 {
  def main(args: Array[String]): Unit = {
//    val sc = SparkUtils.createContext()
//    val lines = sc.textFile("D:\\bbb\\data\\teacher.log")
    System.setProperty("HADOOP_USER_NAME","root")
    //创建SparkContext,只有使用SparkContext才可以向集群申请资源,才可以创建RDD
    val conuts = new SparkConf().setAppName("Conuts").setMaster("local[*]")
    val sc = new SparkContext(conuts)
    val lines = sc.textFile("D:\\bbb\\data\\teacher.log")


    val subjectNameAndOne: RDD[((String, String), Int)] = lines.map(i => {
      //把一行一行提出来,切割
      val fienlds = i.split("/")
      val url = fienlds(2)
      val subject = url.split("[.]")(0)
      val name = fienlds(3)
      ((subject, name), 1)
    })


    //对数据进行聚合
    val reduced: RDD[((String, String), Int)] = subjectNameAndOne.reduceByKey(_ + _)
    //先分组
    val grouped: RDD[(String, Iterable[((String, String), Int)])] = reduced.groupBy(_._1._1)
    //排序
    val result: RDD[(String, (String, Int))] = grouped.flatMapValues(it => {
      //将迭代器中的数据放到内存中
      //这边处理的是v,要把迭代器转化成list,根据数据条数降序排序,返回老师名和数据条数,前两个的
      val list = it.toList
      val sorted: Seq[((String, String), Int)] = list.sortBy(-_._2)
      sorted.map(i => (i._1._2, i._2)).take(3)
    })
    println(result.collect().toBuffer)
    sc.stop()
  }
}

方法二

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

object FavTeachet02 {
  def main(args: Array[String]): Unit = {
    //    val sc = SparkUtils.createContext()
    //    val lines = sc.textFile("D:\\bbb\\data\\teacher.log")
    System.setProperty("HADOOP_USER_NAME","root")
    //创建SparkContext,只有使用SparkContext才可以向集群申请资源,才可以创建RDD
    val conuts = new SparkConf().setAppName("Conuts").setMaster("local[*]")
    val sc = new SparkContext(conuts)
    val lines = sc.textFile("D:\\bbb\\data\\teacher.log")

    val subjectNameAndOne: RDD[((String, String), Int)] = lines.map(i => {
      //把一行一行提出来,切割
      val fienlds = i.split("/")
      val url = fienlds(2)
      val subject = url.split("[.]")(0)
      val name = fienlds(3)
      ((subject, name), 1)
    })


    //对数据进行聚合
    val reduced: RDD[((String, String), Int)] = subjectNameAndOne.reduceByKey(_ + _)
    //把所有的学科放在数组中
    val subjects =Array("bigdata","javaee","php")
    //柯里化指定排序方法
    implicit val ord: Ordering[((String, String), Int)] = Ordering[Int].on[((String, String), Int)](i => -i._2)

     for (sb <- subjects) {
      //过滤掉脏数据,比如切割过为null的
      val filtered: RDD[((String, String), Int)] = reduced.filter(_._1._1.equals(sb))
      val result = filtered.takeOrdered(3)
      println(result.toBuffer)
    }
    sc.stop()
  }
}

方法三

import org.apache.spark.rdd.RDD

import scala.collection.mutable
import scala.collection.parallel.immutable
//true D:\\bbb\\data\\teacher.log 2
object FavTeacher03 {
  def main(args: Array[String]): Unit = {
    //调用自己封装的创建SparkConText方法
    val sc = SparkUtils.createContext(args(0).toBoolean)
    //向集群申请资源
    val lines = sc.textFile(args(1))
    //加入变量topn
    val topN = args(2).toInt

    //http://bigdata.51doit.cn/laozhang
    //把读取到的一行数据进行切割
    val subjectNameAndOne: RDD[((String, String), Int)] = lines.map(i => {
      val fienlds = i.split("/")
      val url = fienlds(2)
      val name = fienlds(3)
      val subject = url.split("\\.")(0)
      ((subject, name), 1)
    })
    //对数据进行聚合
    val reduced: RDD[((String, String), Int)] = subjectNameAndOne.reduceByKey(_ + _)
    //对数据进行分组
    val grouped: RDD[(String, Iterable[((String, String), Int)])] = reduced.groupBy(_._1._1)
    //排序
    val result = grouped.mapValues(f = it => {
      //柯里化指定排序方法
      implicit val ord = Ordering[Int].on[((String, String), Int)](i => -i._2)
      //new 一个TreeSet集合
      val sorter = new mutable.TreeSet[((String, String), Int)]
      //将迭代器的数据一个一个添加到TreeSet集合中
      for (e <- it) {
        sorter += e
        if (sorter.size > topN) {
          sorter -= sorter.last
        }
      }
      sorter
    })
      println(result.collect().toBuffer)
    sc.stop()
  }
}

方法四

import org.apache.spark.Partitioner
import org.apache.spark.rdd.RDD

import scala.collection.mutable

object FavTeacher04 {
  def main(args: Array[String]): Unit = {
    //自己封装的方法创建SparkConText方法
    val sc = SparkUtils.createContext(args(0).toBoolean)
    //获取资源
    val lines = sc.textFile(args(1))
    //加入变量topn
    val topN = args(2).toInt

    //一行一行处理获取过来的数据
    val subjectNameAndOne = lines.map(i => {
      val fienlds = i.split("/")
      val util = fienlds(2)
      val subject = util.split("\\.")(0)
      val name = fienlds(3)
      ((subject,name), 1)
    })
    //对数据进行聚合
    val reduced: RDD[((String, String), Int)] = subjectNameAndOne.reduceByKey(_ + _)
    //先触发一次Action,将学科获取到
    val subjects: Array[String] = reduced.map(_._1._1).distinct().collect()
    //分区,这个只是分区了里面的数据没变
    val partitioned: RDD[((String, String), Int)] = reduced.partitionBy(new SubjectPartitioner(subjects))
    //对分区内的数据进行遍历
    partitioned.foreachPartition(it => {
      //柯里化指定排序方法
      implicit  val ord = Ordering[Int].on[((String, String), Int)](i => -i._2)
      val sorter = new mutable.TreeSet[((String, String), Int)]
      //将迭代器中的数据一条一条的添加到TreeSet中
      it.foreach( e => {
        sorter += e
        if(sorter.size > topN){
          sorter -= sorter.last
        }
      })
      println(sorter.toBuffer)
    })
sc.stop()

  }
  //重写分区器
  class SubjectPartitioner(val subjects:Array[String]) extends Partitioner{
    //定义一个分区规则
    var index=0
    //new一个HashMap把分区和分区编号放在里面
    val nameToIndex = new mutable.HashMap[String, Int]()
    for (elem <- subjects) {
      nameToIndex.put(elem,index)
      index += 1
    }
    //分区的个数
    override def numPartitions: Int = subjects.length
      //取出上面定义好的值(分区编号)返回给方法,就确定了这个学科的分区编号
    override def getPartition(key: Any): Int = {
      //这里转换了下类型,谁调用他就转换成谁的key的类型
      val subject = key.asInstanceOf[(String, String)]._1
      nameToIndex(subject)
    }
  }
}

方法五

import org.apache.spark.Partitioner
import org.apache.spark.rdd.RDD

import scala.collection.mutable

object FavTeacher05 {
  def main(args: Array[String]): Unit = {
    //调用自己封装的方法获取SparkConText
    val sc = SparkUtils.createContext(args(0).toBoolean)
    //获取资源
    val lines = sc.textFile(args(1))
    //定义一个传参的常量.下面会调用
    val topN =args(2).toInt
    //处理一行一行读到的数据
    val subjectNameAndOne = lines.map(i => {
      val fields = i.split("/")
      val url = fields(2)
      val subject = url.split("\\.")(0)
      val name = fields(3)
      ((subject, name), 1)
    })
    //先触发一次Action,将所有学科的信息获取到且去重放到集合里
    val subjects: Array[String] = subjectNameAndOne.map(_._1._1).distinct().collect()
    //对数据惊醒聚合,同时传入分区器,即分区了也聚合了
    val reduced: RDD[((String, String), Int)] = subjectNameAndOne.reduceByKey(new SubjectPartitioner(subjects), _ + _)
    //对分区内的数据进行遍历
    reduced.foreachPartition(it => {
      //柯里化排序
     implicit val ord = Ordering[Int].on[((String, String), Int)](i => i._2).reverse
      //定义一个TreeSet集合
      val sorter = new mutable.TreeSet[((String, String), Int)]()
      //将迭代器中的数据一条一条添加到TreeSet集合中
      it.foreach(e => {
        sorter += e
        if (sorter.size > topN){
          sorter -= sorter.last
        }
      })
      println(sorter.toBuffer)
    })
sc.stop()

  }
  //自定义一个分区器,根据学科分区,所以参数是学科
  class SubjectPartitioner (val subjects:Array[String]) extends Partitioner{
    //定义一个分区规则,先搞个可变的初始值也是第一个分区编号
    var index=0
    //new一个可变HashMap的集合里面装分区和对应的分区编号
    val nameToIndex = new mutable.HashMap[String, Int]()
    for (elem <- subjects) {
      nameToIndex.put(elem,index)
      index += 1
    }
    //分区的个数
    override def numPartitions: Int = subjects.length
    //取出上面定义好的值(分区和编号)返回给方法,确定了分区和对应的分区编号
    override def getPartition(key: Any): Int = {
      //谁调用了他这边就是谁的k,所以要转换成那个类型
      //这边意思就是向根据什么分区就把哪个数据传过来.在通过上面的HashMap就可以获取到所对应的分区
      val subject: String = key.asInstanceOf[(String, String)]._1
      nameToIndex(subject)
    }
  }

}

方法六

import org.apache.spark.Partitioner
import org.apache.spark.rdd.{RDD, ShuffledRDD}

import scala.collection.mutable

object FavTeacher06 {
  def main(args: Array[String]): Unit = {
    //调用自己封装的方法获取SparkConText
    val sc = SparkUtils.createContext(args(0).toBoolean)
    //获取资源
    val lines = sc.textFile(args(1))
//    //定义一个传参的常量.下面会调用
//    val topN =args(2)
    //处理一行一行读到的数据
    val subjectNameAndOne = lines.map(i => {
      val fields = i.split("/")
      val url = fields(2)
      val subject = url.split("\\.")(0)
      val name = fields(3)
      ((subject, name), 1)
    })
    //对数据进行聚合
    val reduced: RDD[((String, String), Int)] = subjectNameAndOne.reduceByKey(_ + _)
    //先触发一次Action,将所有学科的信息获取到且去重放到集合里
    val subjects: Array[String] = subjectNameAndOne.map(_._1._1).distinct().collect()
    //应为分区只能对k进行分区所一要把整体办成k,null为v
    val countSubjectNameAndNull: RDD[((Int, String, String), Null)] = reduced.map(i => ((i._2, i._1._1, i._1._2), null))
    //柯里化排序规则,因为默认对k排序所以不用null了
    implicit val ord = Ordering[(Int, String, String)].reverse
    //分区的同时进行排序
    val result = countSubjectNameAndNull.repartitionAndSortWithinPartitions(new SubjectPartitioner(subjects))

    result.saveAsTextFile("D:\\bbb\\data\\cc")

    //在shuffle的时候进行了分区
    val shuffledRDD: ShuffledRDD[(Int, String, String), Null, Null] = new ShuffledRDD[(Int, String, String), Null, Null](countSubjectNameAndNull, new SubjectPartitioner3(subjects))
    //同时设置排序规则
    shuffledRDD.setKeyOrdering(ord)




    sc.stop()
  }
  //自定义一个分区器,根据学科分区,所以参数是学科
  class SubjectPartitioner (val subjects:Array[String]) extends Partitioner{
    //定义一个分区规则,先搞个可变的初始值也是第一个分区编号
    var index=0
    //new一个可变HashMap的集合里面装分区和对应的分区编号
    val nameToIndex = new mutable.HashMap[String, Int]()
    for (elem <- subjects) {
      nameToIndex.put(elem,index)
      index += 1
    }
    //分区的个数
    override def numPartitions: Int = subjects.length
    //取出上面定义好的值(分区和编号)返回给方法,确定了分区和对应的分区编号
    override def getPartition(key: Any): Int = {
      //谁调用了他这边就是谁的k,所以要转换成那个类型
      //这边意思就是向根据什么分区就把哪个数据传过来.在通过上面的HashMap就可以获取到所对应的分区
      val subject: String = key.asInstanceOf[(Int, String, String)]._2
      nameToIndex(subject)
    }
  }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值