Spark算子

1.RDD方法=>RDD算子

1).转换:功能的补充和封装,将旧的RDD包装成新的RDD(map,flatMap)
2).行动:触发任务的调度和作业的执行(collect)

3).RDD方法=>RDD算子:
RDD(Resilient Distributed Dataset)叫做弹性分布式数据集,是 Spark 中最基本的数据
处理模型。
认知心理学认为解决问题其实将问题的状态进行改变:
问题(初始)=》操作(算子)=》问题(审核中)=》操作(算子) =》问题(完成)
4).算子:Operator(操作)
RDD的方法和Scala的集合对象的方法不一样,集合对象的方法都是在同一个节点的内存中完成的,RDD的方法可以将计算逻辑发送到Executor端(分布式节点)执行。RDD的方法外部的操作都是在Driver端执行的,而方法内部的逻辑代码实在Executor端执行的

2.RDD转换算子

1) map(RDD_Operator_Transform)

 rdd =sc.makeRDD(List(1,2,3,4))
 val mapRDD: RDD[Int] = rdd.map(_ * 2)

小功能:从服务器日志数据apache.log中获取用户请求URL资源路径

val rdd: RDD[String] = sc.textFile("datass/apache.log")


    //长的字符串  转换成短的
    val value: RDD[String] = rdd.map(
      line => {
        val datas: Array[String] = line.split(" ")
        datas(6)
      }
    )

map的分区计算
(1).rdd的计算一个分区内的数据是一个一个执行逻辑
只有墙面一个数据全部的逻辑执行完毕后,才会执行下一个数据
分区内数据的执行时有序的
(2).不同分区数据计算是无序的

2) mapPartations(分区处理map)

➢ 函数签名

def mapPartitions[U: ClassTag](
 f: Iterator[T] => Iterator[U],
 preservesPartitioning: Boolean = false): RDD[U]

➢ 函数说明
将待处理的数据以分区为单位发送到计算节点进行处理,这里的处理是指可以进行任意的处
理,哪怕是过滤数据。

    val sparkConf: SparkConf = new SparkConf().setMaster("local[*]").setAppName("operator")
    //创建向下文环境对象
    val sc = new SparkContext(sparkConf)

    val rdd: RDD[Int] = sc.makeRDD(List(1, 2, 3, 4), 1)
    
    rdd.mapPartitions(
      iter=>{
        iter.map(_*2)
      }
    )

提示:可以以分区为单位进行数据转换操作,但是会将整个分区的数据加载到内存当中进行引用,如果处理完的数据不会被释放掉,存在对象的引用。 在内存比较小,数据量较大的场合下,容易出现内存溢出,此时最好用map

小功能:可以获得每个分区的最大值

	val rdd: RDD[Int] = sc.makeRDD(List(1, 2, 3, 4), 2)
    //有2个分区 ,每个分区的最大值为2 ,4
    val value: RDD[Int] = rdd.mapPartitions(
      iter => {
        List(iter.max).iterator
      }
    )

    value.collect().foreach(println)

思考一个问题:map 和 mapPartitions 的区别? ➢ 数据处理角度
Map 算子是分区内一个数据一个数据的执行,类似于串行操作。而 mapPartitions 算子
是以分区为单位进行批处理操作。
➢ 功能的角度
Map 算子主要目的将数据源中的数据进行转换和改变。但是不会减少或增多数据。
MapPartitions 算子需要传递一个迭代器,返回一个迭代器,没有要求的元素的个数保持不变,
所以可以增加或减少数据
➢ 性能的角度
Map 算子因为类似于串行操作,所以性能比较低,而是 mapPartitions 算子类似于批处
理,所以性能较高。但是 mapPartitions 算子会长时间占用内存,那么这样会导致内存可能
不够用,出现内存溢出的错误。所以在内存有限的情况下,不推荐使用。使用 map 操作。

3) mapPartitionsWithIndex(分区处理map的索引)

➢ 函数签名
def mapPartitionsWithIndex[U: ClassTag](
f: (Int, Iterator[T]) => Iterator[U],
preservesPartitioning: Boolean = false): RDD[U]
➢ 函数说明
将待处理的数据以分区为单位发送到计算节点进行处理,这里的处理是指可以进行任意的处
理,哪怕是过滤数据,在处理时同时可以获取当前分区索引。

val rdd: RDD[Int] = sc.makeRDD(List(1, 2, 3, 4), 2)

    val value: RDD[Int] = rdd.mapPartitionsWithIndex(
      (index, iter: Iterator[Int]) => {
        if (index == 1) {
          iter
        } else Nil.iterator
      }
    )

    value.collect().foreach(println)

小功能:查看数据在哪个分区

val rdd: RDD[Int] = sc.makeRDD(List(1, 2, 3, 4))

    val value: RDD[(Int, Int)] = rdd.mapPartitionsWithIndex(
      (index, iter) => {
        iter.map(num => (index, num))
      }
    )

    value.collect().foreach(println)
(1,1)
(3,2)
(5,3)
(7,4)

4) flatMap(扁平化)

➢ 函数签名
def flatMap[U: ClassTag](f: T => TraversableOnce[U]): RDD[U]
➢ 函数说明
将处理的数据进行扁平化后再进行映射处理,所以算子也称之为扁平映射

val rdd: RDD[List[Int]] = sc.makeRDD(List(List(1, 2, 3, 4), List(5, 6, 7, 8)))


    val value: RDD[Int] = rdd.flatMap(
      (list: List[Int]) => {
        list
      }
    )

    value.collect().foreach(println)

小功能:将 List(List(1,2),3,List(4,5))进行扁平化操作

val rdd: RDD[Any] = sc.makeRDD(List(List(1, 2, 3, 4), 3, List(5, 6, 7, 8)))

    val value = rdd.flatMap(
      data => {
      // 模式匹配
        data match {
          case list:List[_] => list
          case dat => List(dat)
        }
      }
    )

    value.collect().foreach(println)

5) glom(转换数组)

➢ 函数签名
def glom(): RDD[Array[T]]
➢ 函数说明
将同一个分区的数据直接转换为相同类型的内存数组进行处理,分区不变

val rdd: RDD[Int] = sc.makeRDD(List(1, 2, 3, 4),2)

    val glomRDD: RDD[Array[Int]] = rdd.glom()

     glomRDD.collect().foreach(data=>println(data.mkString(",")))
  }

小功能:计算所有分区最大值求和(分区内取最大值,分区间最大值求和)

    val rdd: RDD[Int] = sc.makeRDD(List(1, 2, 3, 4), 2)

    val glomRDD: RDD[Array[Int]] = rdd.glom()

    val maxRDD: RDD[Int] = glomRDD.map(
      array => {
        array.max
      }
    )

    println(maxRDD.collect().sum)

6) groupBy(分组,((a,1),(a,2)))

➢ 函数签名
def groupBy[K](f: T => K)(implicit kt: ClassTag[K]): RDD[(K, Iterable[T])]
➢ 函数说明
将数据根据指定的规则进行分组, 分区默认不变,但是数据会被打乱重新组合,我们将这样
的操作称之为 shuffle。极限情况下,数据可能被分在同一个分区中
一个组的数据在一个分区中,但是并不是说一个分区中只有一个组

小功能:将 List(“Hello”, “hive”, “hbase”, “Hadoop”)根据单词首写字母进行分组。

    val rdd = sc.makeRDD(List("Hello","Spark","Scala","Hadoop"),2)


    val value: RDD[(Char, Iterable[String])] = rdd.groupBy(_.charAt(0))
// rdd.groupBy(_%2)

    value.collect().foreach(println)
(H,CompactBuffer(Hello, Hadoop))
(S,CompactBuffer(Spark, Scala))

小功能:从服务器日志数据 apache.log 中获取每个时间段访问量

val rdd: RDD[String] = sc.textFile("datass/apache.log")
    val timeRDD: RDD[(String, Iterable[(String, Int)])] = rdd.map(
      line => {
        val datas = line.split(" ")
        val time = datas(3)
        val sdf = new SimpleDateFormat("dd/MM/yyyy:HH:mm:ss")
        val date: Date = sdf.parse(time)//解析成日期格式
        val sdf1 = new SimpleDateFormat("HH")
        val hour: String = sdf1.format(date)//转换成字符串
        (hour, 1)
      }
    ).groupBy(_._1)

    timeRDD.map({
      case (hour,iter) =>{(hour,iter.size)}
    }).collect().foreach(println)

7) filter(条件过滤)

➢ 函数签名
def filter(f: T => Boolean): RDD[T]
➢ 函数说明
将数据根据指定的规则进行筛选过滤,符合规则的数据保留,不符合规则的数据丢弃。
当数据进行筛选过滤后,分区不变,但是分区内的数据可能不均衡,生产环境下,可能会出
现数据倾斜。
val dataRDD =

    val rdd: RDD[Int] = sc.makeRDD((List(1, 2, 3, 4)))
    val filterRDD: RDD[Int] = rdd.filter(_ % 2 == 0)
    filterRDD.collect().foreach(println)

小功能:从服务器日志数据 apache.log 中获取 2015 年 5 月 17 日的请求路径

    val rdd: RDD[String] = sc.textFile("datass/apache.log")
    rdd.filter(line=>{
      val datas = line.split(" ")
      val time = datas(3)
      time.startsWith("17/05/2015") //检测字符串是否以指定的前缀开始
    }).collect().foreach(println)
83.149.9.216 - - 17/05/2015:10:05:03 +0000 GET /presentations/logstash-monitorama-2013/images/kibana-search.png
83.149.9.216 - - 17/05/2015:10:05:43 +0000 GET /presentations/logstash-monitorama-2013/images/kibana-dashboard3.png
83.149.9.216 - - 17/05/2015:10:05:47 +0000 GET /presentations/logstash-monitorama-2013/plugin/highlight/highlight.js

8) sample(抽取数据)

e ➢ 函数签名 def sample(
withReplacement: Boolean,
fraction: Double,
seed: Long = Utils.random.nextLong): RDD[T]
➢ 函数说明
根据指定的规则从数据集中抽取数据

val dataRDD = sparkContext.makeRDD(List(
 1,2,3,4
),1)
// 抽取数据不放回(伯努利算法)
// 伯努利算法:又叫 0、1 分布。例如扔硬币,要么正面,要么反面。
// 具体实现:根据种子和随机算法算出一个数和第二个参数设置几率比较,小于第二个参数要,大于不
要
// 第一个参数:抽取的数据是否放回,false:不放回
// 第二个参数:抽取的几率,范围在[0,1]之间,0:全不取;1:全取;
// 第三个参数:随机数种子
val dataRDD1 = dataRDD.sample(false, 0.5)
// 抽取数据放回(泊松算法)
// 第一个参数:抽取的数据是否放回,true:放回;false:不放回
// 第二个参数:重复数据的几率,范围大于等于 0.表示每一个元素被期望抽取到的次数
// 第三个参数:随机数种子
val dataRDD2 = dataRDD.sample(true, 2)

9 )distinct(数据去重)

➢ 函数签名
def distinct()(implicit ord: Ordering[T] = null): RDD[T]
def distinct(numPartitions: Int)(implicit ord: Ordering[T] = null): RDD[T]
➢ 函数说明
将数据集中重复的数据去重

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

10)coalesce(缩减分区)

➢ 函数签名
def coalesce(numPartitions: Int, shuffle: Boolean = false,
partitionCoalescer: Option[PartitionCoalescer] = Option.empty)
(implicit ord: Ordering[T] = null)
: RDD[T]
➢ 函数说明
根据数据量缩减分区,用于大数据集过滤后,提高小数据集的执行效率
当 spark 程序中,存在过多的小任务的时候,可以通过 coalesce 方法,收缩合并分区,减少
分区的个数,减小任务调度成本



val dataRDD = sparkContext.makeRDD(List(
 1,2,3,4,1,2
),6)
val dataRDD1 = dataRDD.coalesce(2)
dataRDD.saveAsTextFile("output")

// coalesce方法默认情况下不会将分区的结果打乱后重新组合
// 这种情况下的缩减分区可能会导致数据不均衡,出现数据倾斜
在这里插入图片描述
// 可以使用第二个参数true, 打开shuffle

思考一个问题:我想要扩大分区,怎么办?

val rdd: RDD[Int] = sc.makeRDD(List(1, 2, 3, 4,5,6),2)
    // 如果想要实现扩大分区的效果,需要打乱数据shuffle
    rdd.coalesce(3,true)

spark提供了一个简化的操作,
缩减分区:coalesce 如果想要数据均衡,可以采用shuffle
扩大分区:repartition ,底层实现代码调用coalesce,肯定开启shuffle

11) repartition(扩大分区)

  1. repartition
    ➢ 函数签名
    def repartition(numPartitions: Int)(implicit ord: Ordering[T] = null): RDD[T]
    ➢ 函数说明
    该操作内部其实执行的是 coalesce 操作,参数 shuffle 的默认值为 true。无论是将分区数多的
    RDD 转换为分区数少的 RDD,还是将分区数少的 RDD 转换为分区数多的 RDD,repartition
    操作都可以完成,因为无论如何都会经 shuffle 过程。

12) sortBy(排序)

➢ 函数签名
def sortBy[K](
f: (T) => K,
ascending: Boolean = true,
numPartitions: Int = this.partitions.length)
(implicit ord: Ordering[K], ctag: ClassTag[K]): RDD[T]
➢ 函数说明
该操作用于排序数据。在排序之前,可以将数据通过 f 函数进行处理,之后按照 f 函数处理
的结果进行排序,默认为升序排列。排序后新产生的 RDD 的分区数与原 RDD 的分区数一
致。中间存在 shuffle 的过程

    val rdd = sc.makeRDD(List(("1", 1),("11",2) ,("2",3)),2)
    val newRDD = rdd.sortBy(_._1.toInt)
    // 默认为升序,加上第二个参数改为false,降序

(1,1)
(2,3)
(11,2)

13)intersection(交集)

➢ 函数签名
def intersection(other: RDD[T]): RDD[T]
➢ 函数说明
对源 RDD 和参数 RDD 求交集后返回一个新的 RDD
val dataRDD1 = sparkContext.makeRDD(List(1,2,3,4))
val dataRDD2 = sparkContext.makeRDD(List(3,4,5,6))
val dataRDD = dataRDD1.intersection(dataRDD2)
思考一个问题:如果两个 RDD 数据类型不一致怎么办?

14) union

➢ 函数签名
def union(other: RDD[T]): RDD[T](并集)
➢ 函数说明
对源 RDD 和参数 RDD 求并集后返回一个新的 RDD
val dataRDD1 = sparkContext.makeRDD(List(1,2,3,4))
val dataRDD2 = sparkContext.makeRDD(List(3,4,5,6))
val dataRDD = dataRDD1.union(dataRDD2)
思考一个问题:如果两个 RDD 数据类型不一致怎么办?

15) subtract(差集)

➢ 函数签名
def subtract(other: RDD[T]): RDD[T]
➢ 函数说明
以一个 RDD 元素为主,去除两个 RDD 中重复元素,将其他元素保留下来。求差集

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

思考一个问题:如果两个 RDD 数据类型不一致怎么办?

16) zip(拉链)

➢ 函数签名
def zip[U: ClassTag](other: RDD[U]): RDD[(T, U)]
➢ 函数说明
将两个 RDD 中的元素,以键值对的形式进行合并。其中,键值对中的 Key 为第 1 个 RDD
中的元素,Value 为第 2 个 RDD 中的相同位置的元素。
数据类型可以不一致

    val dataRDD1 = sc.makeRDD(List(1,2,3,4))
    val dataRDD2 = sc.makeRDD(List(3,4,5,6))
    val rdd1 = dataRDD1.intersection(dataRDD2)
    println(rdd1.collect().mkString(","))
    val rdd2 = dataRDD1.union(dataRDD2)
    println(rdd2.collect().mkString(","))
    val rdd3 = dataRDD1.subtract(dataRDD2)
    val rdd4 = dataRDD2.subtract(dataRDD1)
    println(rdd3.collect().mkString(","))
    println(rdd4.collect().mkString(","))

    println("----------")
    //拉链
    val rdd6: RDD[(Int, Int)] = dataRDD1.zip(dataRDD2)
    println(rdd6.collect().mkString(","))

3,4
1,2,3,4,3,4,5,6
1,2
5,6
(1,3),(2,4),(3,5),(4,6)


思考一个问题:如果两个 RDD 数据类型不一致怎么办?
数据类型可以不一致
思考一个问题:如果两个 RDD 数据分区不一致怎么办?
报错呗
思考一个问题:如果两个 RDD 分区数据数量不一致怎么办?
报错呗

⚫ Key - Value 类型

17) partitionBy(重新分区)

➢ 函数签名
def partitionBy(partitioner: Partitioner): RDD[(K, V)]
➢ 函数说明
将数据按照指定 Partitioner 重新进行分区。Spark 默认的分区器是 HashPartitioner

    val rdd: RDD[Int] = sc.makeRDD(List(1, 2, 3, 4),2)
    val mapRDD: RDD[(Int, Int)] = rdd.map((_, 1))
    // partitionBy属于PairRDDFunctions的方法
    // 将 RDD类型=>PairRDDFunctions
    // 隐式转换(二次编译)
    //根据指定的分区规则对数据进行分区
    mapRDD.partitionBy(new HashPartitioner(2)).saveAsTextFile("output")
    //第一个区(2,1),(4,1)
    //第二个区(1,1),(3,1)

思考一个问题:如果重分区的分区器和当前 RDD 的分区器一样怎么办?
如果类型相等,数量相等 ,它会返回自己,不做操作
思考一个问题:Spark 还有其他分区器吗?
RangePartitoner
思考一个问题:如果想按照自己的方法进行数据分区怎么办?
自定义
思考一个问题:哪那么多问题?

18) reduceByKey(分组聚合,且分区内,间计算相同,一个参数)

➢ 函数签名
def reduceByKey(func: (V, V) => V): RDD[(K, V)]
def reduceByKey(func: (V, V) => V, numPartitions: Int): RDD[(K, V)]
➢ 函数说明
可以将数据按照相同的 Key 对 Value 进行聚合

    //两两聚合
    val reduceRDD: RDD[(String, Int)] = rdd.reduceByKey((x, y) => x + y)
    //简化:
//    rdd.reduceByKey(_+_)

(a,7)
(b,3)

如果reduceBykey中如果key的数据只有一个,是不会参与运算的

19) groupByKey(分组,(a,(1,2,3))

➢ 函数签名
def groupByKey(): RDD[(K, Iterable[V])]
def groupByKey(numPartitions: Int): RDD[(K, Iterable[V])]
def groupByKey(partitioner: Partitioner): RDD[(K, Iterable[V])]
➢ 函数说明
将数据源的数据根据 key 对 value 进行分组

    val rdd = sc.makeRDD(List(("a",1), ("a",2), ("b",3), ("a",4)))

    val groupRDD: RDD[(String, Iterable[Int])] = rdd.groupByKey()
    val groupRDD1: RDD[(String, Iterable[(String, Int)])] = rdd.groupBy(_._1)
    groupRDD.collect().foreach(println)
    groupRDD1.collect().foreach(println)

对比和groupby的区别

(a,CompactBuffer(1, 2, 4))
(b,CompactBuffer(3))
(a,CompactBuffer((a,1), (a,2), (a,4)))
(b,CompactBuffer((b,3)))

思考一个问题:reduceByKey 和 groupByKey 的区别?
从 shuffle 的角度:reduceByKey 和 groupByKey 都存在 shuffle 的操作,但是 reduceByKey
可以在 shuffle 前对分区内相同 key 的数据进行预聚合(combine)功能,这样会减少**落盘(缓冲区)**的
数据量,而 groupByKey 只是进行分组,不存在数据量减少的问题,reduceByKey 性能比较
高。

groupByKey:
在这里插入图片描述
reduceByKey:
在这里插入图片描述

从功能的角度:reduceByKey 其实包含分组和聚合的功能。GroupByKey 只能分组,不能聚
合,所以在分组聚合的场合下,推荐使用 reduceByKey,如果仅仅是分组而不需要聚合。那
么还是只能使用 groupByKey

20) 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), ("a",3), ("a",4)),2)

    //(a,[1,2]),(a,[3,4]) 取最大值
    //(a,2),(a,4)  求和
    //(a,6)
    // 第一个参数列表需要传递一个参数,有一个参数 表示为初始值
    //        主要用于当碰见第一个key的时候,和value进行分区计算
    //第二个参数列表需要传递两个参数:
    //     第一个参数列表:表示分区内计算规则
    //     第二个参数列表:表示分区间计算规则,传递两个参数
    rdd.aggregateByKey(0)(
      (x,y)=> math.max(x,y),
      (x,y)=> x+y
    ).collect().foreach(println)

(a,6)

小练习: 获取相同key的value的平均值

    val rdd = sc.makeRDD(List(("a",1), ("a",2), ("b",3), ("a",4)),2)
    //  获取相同key的value的平均值
    val aggRDD: RDD[(String, (Int, Int))] = rdd.aggregateByKey((0, 0))(
      //分区内计算
      // 第一个参数为tuple(0,0) 第一个0表示value值,第二个0表示出现的次数
      (t, v) => {
        (t._1 + v, t._2 + 1)
      },
      //分区间计算 ,value值相加。次数相加
      (t1, t2) => {
        (t1._1 + t2._1, t1._2 + t2._2)
      }
    )

    aggRDD.mapValues{
          //模式匹配可以将tuple的值赋给变量sum,num,可读性比t._1,t._2好多了
      case (num,cnt)=>
      (num/cnt)
    }.collect().foreach(println)

21) foldByKey(分组聚合,且分区内,间计算相同,2个参数)

➢ 函数签名
def foldByKey(zeroValue: V)(func: (V, V) => V): RDD[(K, V)]
➢ 函数说明
当分区内计算规则和分区间计算规则相同时,aggregateByKey 就可以简化为 foldByKey

val dataRDD1 = sparkContext.makeRDD(List(("a",1),("b",2),("c",3)))
val dataRDD2 = dataRDD1.foldByKey(0)(_+_)

22) combineByKey(分组聚合,三个参数,分区内、分区间计算不同)

➢ 函数签名
def combineByKey[C](
createCombiner: V => C,
mergeValue: (C, V) => C,
mergeCombiners: (C, C) => C): RDD[(K, C)]
➢ 函数说明
最通用的对 key-value 型 rdd 进行聚集操作的聚集函数(aggregation function)。类似于
aggregate(),combineByKey()允许用户返回值的类型与输入不一致。
小练习:将数据 List((“a”, 88), (“b”, 95), (“a”, 91), (“b”, 93), (“a”, 95), (“b”, 98))求每个 key 的平
均值

    val rdd = sc.makeRDD(List(("a",1), ("a",2), ("b",3), ("a",4)),2)
    // 第一个参数表示:将相同key的第一个数据进行结构转换,实现操作
    // 第二个参数表示:分区内的计算规则
    //第三个参数表示:分区间的计算规则

    val comRDD: RDD[(String, (Int, Int))] = rdd.combineByKey(
      v => (v, 1),
      (t: (Int, Int), v) => {
        (t._1 + v, t._2 + 1)
      },
      (t1: (Int, Int), t2: (Int, Int)) => (t1._1 + t2._1, t2._2 + t2._2))
    comRDD.mapValues(t=>t._1/t._2).collect().foreach(println)

(b,3)
(a,3)

reduceByKey、foldByKey、aggregateByKey、combineByKey 的区别?

reduceByKey: 相同 key 的第一个数据不进行任何计算,分区内和分区间计算规则相同
FoldByKey: 相同 key 的第一个数据和初始值进行分区内计算,分区内和分区间计算规则相
同。
AggregateByKey:相同 key 的第一个数据和初始值进行分区内计算,分区内和分区间计算规
则可以不相同
CombineByKey:当计算时,发现数据结构不满足要求时,可以让第一个数据转换结构。分区
内和分区间计算规则不相同。

    val rdd = sc.makeRDD(List(("a",1), ("a",2), ("b",3), ("a",4)),2)
    // wordcount各种实现:
    rdd.reduceByKey(_+_)
    rdd.aggregateByKey(0)(_+_,_+_)
    rdd.foldByKey(0)(_+_)
    rdd.combineByKey(v=>v,(x:Int,y)=>x+y,(x:Int,y:Int)=>x+y)

    /* reduceByKey : 
              combineByKeyWithClassTag[V](
                (v: V) => v,   //第一个值不会参与计算
                func,          //分区内计算规则
                func,          //分区间计算规则
                )
         
       aggregateByKey:
            combineByKeyWithClassTag[U](
                (v: V) => cleanedSeqOp(createZero(), v),// 相同key的第一条数据的处理函数
                cleanedSeqOp,//分区内计算规则
                combOp,  //分区间计算规则
               )
       
       foldByKey:
            combineByKeyWithClassTag[V](
              (v: V) => cleanedFunc(createZero(), v), // 相同key的第一条数据的处理函数
              cleanedFunc, //分区内计算规则
              cleanedFunc, //分区间计算规则
      )
      
      
       combineByKey:      
                combineByKeyWithClassTag(
                createCombiner, // 相同key的第一条数据的处理函数
                mergeValue, //分区内数据的处理函数
                mergeCombiners, /分区间数据的处理函数
      
      )
 
 */

23) sortByKey(根据key进行排序)

➢ 函数签名
def sortByKey(ascending: Boolean = true, numPartitions: Int = self.partitions.length)
: RDD[(K, V)]
➢ 函数说明
在一个(K,V)的 RDD 上调用,K 必须实现 Ordered 接口(特质),返回一个按照 key 进行排序

    val dataRDD1 = sc.makeRDD(List(("a",1),("a",2),("b",2),("c",3)))
    val sortRDD1: RDD[(String, Int)] = dataRDD1.sortByKey(true)
    val sortRDD2: RDD[(String, Int)] = dataRDD1.sortByKey(false)
    sortRDD1.collect().foreach(println)
    sortRDD2.collect().foreach(println)

(a,1)
(a,2)
(b,2)
(c,3)
(c,3)
(b,2)
(a,1)
(a,2)

24) join(内连接,乘)

➢ 函数签名
def join[W](other: RDD[(K, W)]): RDD[(K, (V, W))]
➢ 函数说明
在类型为(K,V)和(K,W)的 RDD 上调用,返回一个相同 key 对应的所有元素连接在一起的
(K,(V,W))的 RDD

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)

(1,(a,4))
(2,(b,5))
(3,(c,6))
思考一个问题:如果 key 存在不相等呢?
如果两个数据源的key没有匹配上,那么数据不会出现在结果中

如果key比较多,他会挨个匹配,数据量会几何性增长,会导致性能降低
在这里插入图片描述

25) leftOuterJoin

➢ 函数签名
def leftOuterJoin[W](other: RDD[(K, W)]): RDD[(K, (V, Option[W]))]
➢ 函数说明
类似于 SQL 语句的左外连接

    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), (4, 6)))
    rdd.leftOuterJoin(rdd1).collect().foreach(println)

(1,(a,Some(4)))
(2,(b,Some(5)))
(3,(c,None))

26) cogroup(分组连接)

➢ 函数签名
def cogroup[W](other: RDD[(K, W)]): RDD[(K, (Iterable[V], Iterable[W]))]
➢ 函数说明
在类型为(K,V)和(K,W)的 RDD 上调用,返回一个(K,(Iterable,Iterable))类型的 RDD
理解为:connect+group

    val rdd1: RDD[(String, Int)] = sc.makeRDD(List(("a", 4), ("b", 2), ("c", 3)))
    val rdd2: RDD[(String, Int)] = sc.makeRDD(List(("a", 4), ("b", 1), ("c", 3),("c",6)))
    val cgRDD: RDD[(String, (Iterable[Int], Iterable[Int]))] = rdd1.cogroup(rdd2)
    cgRDD.collect().foreach(println)

(a,(CompactBuffer(4),CompactBuffer(4))) (b,(CompactBuffer(2),CompactBuffer(1)))
(c,(CompactBuffer(3),CompactBuffer(3, 6)))

转换算子 case1

  1. 数据准备
    agent.log:时间戳,省份,城市,用户,广告,中间字段使用空格分隔。

  2. 需求描述
    统计出每一个省份每个广告被点击数量排行的 Top3

  3. 需求分析
    在这里插入图片描述
    在这里插入图片描述

  4. 功能实现

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

object Spark0x_RDD_Case01 {

  def main(args: Array[String]): Unit = {
       // 建立链接
      val sparkConf = new SparkConf().setMaster("local[*]").setAppName("Operator")
      val sc = new SparkContext(sparkConf)

    //1.获取原始数据:时间戳...
    val dataRDD: RDD[String] = sc.textFile("datass/agent.log")

    //2.将原始数据进行结构转换,方便统计
    // => ((省份,广告),1)

    val mapRDD: RDD[((String, String), Int)] = dataRDD.map(
      line => {
        val data: Array[String] = line.split(" ")
        ((data(1), data(4)), 1)

      }
    )
    //3.将转换结构的数据,进行分组聚合
    //((省份,广告),1)=> ((省份,广告),sum)
    val reduceRDD: RDD[((String, String), Int)] = mapRDD.reduceByKey(_ + _)
    //4将聚合的结果进行结构的转换
    //((省份,广告),sum)=>(省份,(广告,sum))
    val newRDD: RDD[(String, (String, Int))] = reduceRDD.map {
      case ((prv, ad), sum) => (prv, (ad, sum))
    }

    //5.将转换结构后的数据根据省份进行分组
    //(省份,【(广告A,sumA)),((广告B,sumB)))
    val groupRDD: RDD[(String, Iterable[(String, Int)])] = newRDD.groupByKey()

    //6.将分组后的数据组内排序,降序,取前三名
    val resultRDD: RDD[(String, List[(String, Int)])] = groupRDD.mapValues(
      iter => {
        iter.toList.sortBy(_._2)(Ordering[Int].reverse).take(3)
      }
    )

    //7.采集数据打印在控制台
    resultRDD.collect().foreach(println)
  }

}

2.RDD行动算子

//所谓的行动算子,其实就是触发作业(Job)执行的方法
//底层代码调用的是环境对象的runJob方法
//底层代码会创建ActiveJob,并提交执行

1) reduce

➢ 函数签名
def reduce(f: (T, T) => T): T
➢ 函数说明
聚集 RDD 中的所有元素,先聚合分区内数据,再聚合分区间数据

    val rdd: RDD[Int] = sc.makeRDD(List(1, 2, 3, 4))

    val i: Int = rdd.reduce(_ + _)
    println(i)

10

2) collect

➢ 函数签名
def collect(): Array[T]
➢ 函数说明
在驱动程序中,以数组 Array 的形式返回数据集的所有元素,
该方法会将不同分区的数据按照分区顺序采集到Driver端内存中,形成数组

    val rdd: RDD[Int] = sc.makeRDD(List(1, 2, 3, 4))

    val i: Array[Int] = rdd.collect()
    println(i.mkString(","))

1,2,3,4

3) count(数据的个数)

➢ 函数签名
def count(): Long
➢ 函数说明
返回 RDD 中元素的个数

4) first

➢ 函数签名
def first(): T
➢ 函数说明
返回 RDD 中的第一个元素

5) take(前n个)

➢ 函数签名
def take(num: Int): Array[T]
➢ 函数说明
返回一个由 RDD 的前 n 个元素组成的数组

// 返回 RDD 中元素的个数
val takeResult: Array[Int] = rdd.take(2)
println(takeResult.mkString(","))

6) takeOrdered(先排序,后取前n个)

➢ 函数签名
def takeOrdered(num: Int)(implicit ord: Ordering[T]): Array[T]
➢ 函数说明
返回该 RDD 排序后的前 n 个元素组成的数组

val rdd: RDD[Int] = sc.makeRDD(List(1,3,2,4))
// 返回 RDD 中元素的个数
val result: Array[Int] = rdd.takeOrdered(2)

7) aggregate

➢ 函数签名
def aggregate[U: ClassTag](zeroValue: U)(seqOp: (U, T) => U, combOp: (U, U) => U): U
➢ 函数说明
分区的数据通过初始值和分区内的数据进行聚合,然后再和初始值进行分区间的数据聚合

val rdd: RDD[Int] = sc.makeRDD(List(1, 2, 3, 4),2)


    val result: Int = rdd.aggregate(0)(_ + _, _ + _)

    println(result)

10

和aggregateByKey的区别

	//预测:13+17 =30
    // aggregateBykey的初始值只会参与分区内计算
    //aggregate的初始值会参数分区内和分区间的计算
    //实际13+17+10=40
    val result: Int = rdd.aggregate(10)(_ + _, _ + _)
    
    println(result)

8) fold

➢ 函数签名
def fold(zeroValue: T)(op: (T, T) => T): T
➢ 函数说明
折叠操作,aggregate 的简化版操作

val rdd: RDD[Int] = sc.makeRDD(List(1, 2, 3, 4))
val foldResult: Int = rdd.fold(0)(_+_)

9) countByKey

➢ 函数签名
def countByKey(): Map[K, Long]
➢ 函数说明
统计每种 key 的个数
countByValue

val rdd: RDD[Int] = sc.makeRDD(List(1, 2, 3, 4),2)
val map: collection.Map[Int, Long] = rdd.countByValue()
println(map)

countByKey

    val rdd: RDD[(String, Int)] = sc.makeRDD(List(("a", 1), ("a", 2), ("b", 1)))
    val map: collection.Map[String, Long] = rdd.countByKey()
    println(map

Map(a -> 2, b -> 1)

WordCount实现

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

import scala.collection.mutable

object Spark0x_RDD_WordCount {
  def main(args: Array[String]): Unit = {
    val sparkConf: SparkConf = new SparkConf().setMaster("local[*]").setAppName("operator")
    //创建向下文环境对象
    val sc = new SparkContext(sparkConf)


    sc.stop()
  }

  def wordcount1(sc:SparkContext)={
    val rdd: RDD[String] = sc.makeRDD(List("Hello Scala", "Hello Spark"))
    val fmRDD: RDD[String] = rdd.flatMap(_.split(" "))
    val group: RDD[(String, Iterable[String])] = fmRDD.groupBy(word => word)
    val wordCount: RDD[(String, Int)] = group.mapValues(iter => iter.size)

  }
  def wordcount2(sc:SparkContext)={
    val rdd: RDD[String] = sc.makeRDD(List("Hello Scala", "Hello Spark"))
    val words: RDD[String] = rdd.flatMap(_.split(" "))
    val wordOne: RDD[(String, Int)] = words.map((_, 1))
    val groupRDD: RDD[(String, Iterable[Int])] = wordOne.groupByKey()
    val wordCount: RDD[(String, Int)] = groupRDD.mapValues(iter => iter.size)
  }


  //reduceByKey
  def wordcount3(sc:SparkContext)={
    val rdd: RDD[String] = sc.makeRDD(List("Hello Scala", "Hello Spark"))
    val words: RDD[String] = rdd.flatMap(_.split(" "))
    val wordOne: RDD[(String, Int)] = words.map((_, 1))
    val groupRDD: RDD[(String, Int)] = wordOne.reduceByKey(_+_)
  }


  //aggregateByKey
  def wordcount4(sc:SparkContext)={
    val rdd: RDD[String] = sc.makeRDD(List("Hello Scala", "Hello Spark"))
    val words: RDD[String] = rdd.flatMap(_.split(" "))
    val wordOne: RDD[(String, Int)] = words.map((_, 1))
    val groupRDD: RDD[(String, Int)] = wordOne.aggregateByKey(0)(_+_,_+_)
  }


  //foldByKey
  def wordcount5(sc:SparkContext)={
    val rdd: RDD[String] = sc.makeRDD(List("Hello Scala", "Hello Spark"))
    val words: RDD[String] = rdd.flatMap(_.split(" "))
    val wordOne: RDD[(String, Int)] = words.map((_, 1))
    val groupRDD: RDD[(String, Int)] = wordOne.foldByKey(0)(_+_)
  }

  // combineByKey
  def wordcount6(sc:SparkContext)={
    val rdd: RDD[String] = sc.makeRDD(List("Hello Scala", "Hello Spark"))
    val words: RDD[String] = rdd.flatMap(_.split(" "))
    val wordOne: RDD[(String, Int)] = words.map((_, 1))
    val groupRDD: RDD[(String, Int)] = wordOne.combineByKey(v=>v,_+_,_+_)
  }

  def wordcount7(sc:SparkContext)={
    val rdd: RDD[String] = sc.makeRDD(List("Hello Scala", "Hello Spark"))
    val words: RDD[String] = rdd.flatMap(_.split(" "))
    val wordOne: RDD[(String, Int)] = words.map((_, 1))
    val wordCount: collection.Map[String, Long] = wordOne.countByKey()
  }

  def wordcount8(sc:SparkContext)={
    val rdd: RDD[String] = sc.makeRDD(List("Hello Scala", "Hello Spark"))
    val words: RDD[String] = rdd.flatMap(_.split(" "))
    val wordCount: collection.Map[String, Long] = rdd.countByValue()
  }


  //reduce
  def wordcount9(sc:SparkContext)={
    val rdd: RDD[String] = sc.makeRDD(List("Hello Scala", "Hello Spark"))
    val words: RDD[String] = rdd.flatMap(_.split(" "))
    val mapWord: RDD[mutable.Map[String, Long]] = words.map(
      word => {
        mutable.Map[String, Long]((word, 1))
      }
    )

    val wordCount: mutable.Map[String, Long] = mapWord.reduce(
      (map1: mutable.Map[String, Long], map2) => {
        map2.foreach({
          case (word, count) => {
            val newCount: Long = map1.getOrElse(word, 0L) + count
            map1.update(word, newCount)
          }
        })
        map1
      }
    )
  }

10) save 相关算子

➢ 函数签名
def saveAsTextFile(path: String): Unit
def saveAsObjectFile(path: String): Unit
def saveAsSequenceFile(
path: String,
codec: Option[Class[_ <: CompressionCodec]] = None): Unit
➢ 函数说明
将数据保存到不同格式的文件中

val rdd: RDD[(String, Int)] = sc.makeRDD(List(("a", 1), ("a", 2), ("b", 1)))
    rdd.saveAsTextFile("output")
    rdd.saveAsObjectFile("output1")
    //saveAsSequenceFile要求数据的格式必须为K——V类型
    rdd.saveAsSequenceFile("output2")

11) foreach(分布式遍历)

➢ 函数签名
def foreach(f: T => Unit): Unit = withScope {
val cleanF = sc.clean(f)
sc.runJob(this, (iter: Iterator[T]) => iter.foreach(cleanF))
}
➢ 函数说明
分布式遍历 RDD 中的每一个元素,调用指定函数

val rdd: RDD[(String, Int)] = sc.makeRDD(List(("a", 1), ("a", 2), ("b", 1)))
    // 收集后打印
    // 这个foreach其实是Driver端内存数据集合的循环遍历方式
    rdd.collect().foreach(println)
    println("****************")
    //这个foreach其实是Executor端内存数据打印 没有按照顺序采集
    rdd.foreach(println)

(a,1)
(a,2)
(b,1)
(a,2)
(a,1)
(b,1)

在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值