Spark(二)----RDD

简介

Spark是一个大数据分布式并行计算框架,不仅实现了MapReduce的算子,map函数和reduce函数形成了一个计算模型。还提供了更加丰富的算子,Spark中提到的算子的概念就可以简称为RDD。

RDD叫做分布式数据集,是Spark中最基本的数据抽象,它是一个不可变,可分区,里面的元素可以并行计算的集合。

RDD数据流模型的特点

1)自动容错
2)位置感知
3)可伸缩

RDD允许在执行多个查询时显示的将工作集缓存到内存中,后续的查询能够重用这个工作集,这样可以提高效率。

ps:Spark和很多其他分布式计算系统都借用了 “分而治之”思想来实现并行处理:
把一个超大的数据集,切分成N个小堆,找M个执行器,各自拿一块或多块数据执行,执行出结果后再进行汇合,spark的工作就是:凡是能被我处理的,都要符合我的严格要求,所以Spark无论在处理什么数据都会先整合成一个拥有多个分块的 数据集,而这个数据集就是RDD。

RDD就像操作本地集合一样,很多的方法可以调用(算子),使用方便,无序关系,底层实现

val lines:RDD[String] = sc.textFile(“路径”)
通过这个方法(算子)可以返回的数据类型是RDD,可以想成通过testFile这个方法获取文件中的数据存储到RDD类型中。

深入一些

实际RDD是一个引用(指针),引用这个文件,这个文件中的一些信息可以通过这个引用来进行操作。

在这里插入图片描述

Spark将分布式数据抽象为弹性分布式数据集(RDD),实现了应用程序之间的调度----RPC,序列化和反序列化,压缩,并未运行在其上的上层组件提供API,其底层实现采用了Scala这种函数式语言书写而成,并且所提供的API深度借鉴了Scala函数式编程思想,提供了Scala类型的编程接口。

RDD指的是一个只读,可分区的分布式数据集,这个数据集的全部或者部分可以缓存到内存中,并可以多次在计算中重用。

RDD叫做分布式数据集,是Spark中的基本数据抽象,它代表一个不可变,不分区 里面的元素,可以并行计算

RDD的五大特征

1.RDD可以看做是一系列 partition所组成的
2.RDD之间有依赖关系
3.算子是作用在partition之上的
4.分区是做用在KV形式的RDD上
5.partition提供的最佳计算位置,利于数据处理的本地化,计算向数据移动,而不是移动数据。

RDD本身是不存储数据的,这里为了方便理解,暂时理解成了存储数据
ps:RDD 是一个引用数据的

什么是KV格式的RDD

如果RDD里面存的数据都是二元组对象(“k”,“v”),那么这个RDD我们就叫做KV格式的RDD

RDD的容错
partition数量,大小是没有限制,体现了RDD的弹性特质
RDD之间的依赖关系,可以给予上一个RDD重新计算出RDD

RDD的分布式
RDD是有partition组成,Partition是分布在不同节点之上,RDD提供计算的位置,体现了数据本地化,计算向数据移动

详细的RDD特征

RDD的属性

RDD算子

RDD算子可以分为两种类型
1)Transformation(转换)
RDD中的所谓转换都是延迟加载,也就是他们不会直接 计算结果,相反,它们只是机组这些用应用到基础数据集上的转换动作,只有当发生一个要求返回结果给Driver的动作时,这些转换才会真正的运行,这样的设计可以让spark更加有效率的运行。
在这里插入图片描述
在这里插入图片描述

2)Action(动作):在RDD上运行计算,并返回结果给Driver或写入文件系统
在这里插入图片描述

ps:一个Action相当于是一个job

Transformation属于延迟计算,当一个RDD转换为另一个RDD的时候并没有立即转换,仅仅是记住了数据集的逻辑操作,当Action出发Spark作业的时候,才会真正的执行。

算子简单使用

1) 初级算子
PrimaryOperatorDemo.java

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

object PrimaryOperatorDemo {

  def main(args: Array[String]): Unit = {
    /**
      * 因为spark-shell 可以分为两种运行模式
      * 集群模式:spark-shell --master spark:..hadoop1:7077
      * --executor-memeory 内存m --total--executor-cores 核数
      * 本地模式: spark-shell
      * 无论是本地还是集群都已经创建好了两个变量
      * sparkContxt -->sc sqlContext-->sqlContext
     */
    //在IDEA中需要自己创建sc对象并且指定运行模式
    val conf = new SparkConf().setAppName("PrimaryOperatorDemo1").setMaster("local")
    val sc = new SparkContext(conf)

    //1.通过并行化生成RDD
    val rdd = sc.parallelize(List(5,6,4,7,3,8,2,9,10))
    //2.对rdd里面的每个元素*2 然后排序
    /**
      * sortBy:
      * 第一个参数是遍历,第二个参数是排序方式,true--代表升序
      * 会受线程影响
      */
    val rdd2: RDD[Int] = rdd.map(_  * 2).sortBy(x => x,true)
    println(rdd2.collect().toBuffer)//ArrayBuffer(4, 6, 8, 10, 12, 14, 16, 18, 20)

    //3.过滤出结果大于10
    val rdd3: RDD[Int] = rdd2.filter(_ > 10)
    println(rdd3.collect().toBuffer)//ArrayBuffer(12, 14, 16, 18, 20)

    //4.通过并行化生成新的RDD
    val rdd4: RDD[String] = sc.parallelize(Array("a b c","b c d"))
    //将rdd4中的数据切分后压平
    val rdd5: RDD[String] = rdd4.flatMap(_.split(" "))
    println(rdd5.collect().toBuffer)//ArrayBuffer(a, b, c, b, c, d)

    //6.假如List(List("a,b","b c"),List("e c","i o"))
    //压平 flatMap(_.flatMap(_.split(" ")))
    val list: RDD[List[String]] = sc.parallelize(List(List("a b","b c"),List("e c","i o")))
    val list1: RDD[String] = list.flatMap(_.flatMap(_.split(" ")))
    println(list1.collect().toBuffer)//ArrayBuffer(a, b, b, c, e, c, i, o)

    //7.求并集
    val rdd6: RDD[Int] = sc.parallelize(List(5,6,7,8))
    val rdd7: RDD[Int] = sc.parallelize(List(1,2,5,6))
    val rdd8: RDD[Int] = rdd6 union rdd7
    println(rdd8.collect.toBuffer)//ArrayBuffer(5, 6, 7, 8, 1, 2, 5, 6)

    //8.求交集
    val rdd9: RDD[Int] = rdd6 intersection rdd7
    println(rdd9.collect().toBuffer)//ArrayBuffer(6, 5)

    //9.去重 去重复
    println(rdd8.distinct.collect.toBuffer)//ArrayBuffer(6, 8, 2, 1, 7, 5)

    //10.join--只有有相同的才能连接,没有不显示
    //通过并行化生成RDD
    val rdd10_1: RDD[(String, Int)] = sc.parallelize(List(("tom",1),("jerry",3),("kitty",2)))
    val rdd10_2: RDD[(String, Int)] = sc.parallelize(List(("jerry",2),("tom",2),("dog",10)))
    //相同的key会被合并
    val rdd10_3: RDD[(String, (Int, Int))] = rdd10_1 join rdd10_2
    println(rdd10_3.collect().toBuffer)//ArrayBuffer((tom,(1,2)), (jerry,(3,2)))

    //11.左连接和右连接
    //除了基准值外是Option类型,因为可能存在空值所以使用Option
    val rdd10_4: RDD[(String, (Int, Option[Int]))] = rdd10_1 leftOuterJoin rdd10_2 //以左边为基准没有是null
    val rdd10_5: RDD[(String, (Option[Int], Int))] = rdd10_1 rightOuterJoin rdd10_2 //以右边为基准没有是null
    println(rdd10_4.collect().toList) //List((tom,(1,Some(2))), (jerry,(3,Some(2))), (kitty,(2,None)))
    println(rdd10_5.collect().toBuffer) //ArrayBuffer((tom,(Some(1),2)), (dog,(None,10)), (jerry,(Some(3),2)))

    //12.求一个并集
    val rdd11: RDD[(String, Int)] = rdd10_1 union  rdd10_2
    println(rdd11.collect().toList)//List((tom,1), (jerry,3), (kitty,2), (jerry,2), (tom,2), (dog,10))

    //按照key进行分组
    val rdd11_1: RDD[(String, Iterable[(String, Int)])] = rdd11.groupBy(_._1)
    println(rdd11_1.collect().toBuffer)//ArrayBuffer((tom,CompactBuffer((tom,1), (tom,2))), (dog,CompactBuffer((dog,10))), (jerry,CompactBuffer((jerry,3), (jerry,2))), (kitty,CompactBuffer((kitty,2))))

    //按照key进行分组,并且可以指定分区
    val rdd11_1_1: RDD[(String, Iterable[Int])] = rdd11.groupByKey
    println(rdd11_1_1.collect.toList)//List((tom,CompactBuffer(1, 2)), (dog,CompactBuffer(10)), (jerry,CompactBuffer(3, 2)), (kitty,CompactBuffer(2)))

    //cogroup合并数据并根据相同key进行排序
    //cogroup和groupBykey的区别
    //cogroup输入的数据必须是(k,v)和另外一个(k,w)得到一个(k,(seq[v],seq[w]))的数据集
    //groupByKey: 进行对已经合并好的数据根据相同key进行分组,得到一个(k,seq[v])
    //分组的话要提供二元组(k,v)
    val rdd12_1 = sc.parallelize(List(("tom",1),("jerry",3),("kitty",2)))
    val rdd12_2 = sc.parallelize(List(("jerry",2),("tom",2),("dog",10)))
    val rdd12_3: RDD[(String, (Iterable[Int], Iterable[Int]))] = rdd12_1.cogroup(rdd12_2)
    println(rdd12_3.collect.toBuffer)//ArrayBuffer((tom,(CompactBuffer(1),CompactBuffer(2))), (dog,(CompactBuffer(),CompactBuffer(10))), (jerry,(CompactBuffer(3),CompactBuffer(2))), (kitty,(CompactBuffer(2),CompactBuffer())))

    //相同key的value进行计算
    val rdd13_2 = rdd11.reduceByKey((x,y) => x+y)
    println(rdd13_2.collect.toList)//List((tom,3), (dog,10), (jerry,5), (kitty,2))

    //求和
    val rdd14 = sc.parallelize(List(1,2,3,4,5,6))
    val rdd14_1 = rdd14.reduce(_+_)
    println(rdd14_1)

    //需求:List(("tom",1),("jerrry",3),("kitty",2)) List(("jerry",2),("tom",2),("dog",10))
    //需要先合并数据,按照key进行聚合,根据key进行排序(根据数值而非字符串)
    val rdd15_1 = sc.parallelize(List(("tom",1),("jerry",3),("kitty",2)))
    val rdd15_2 = sc.parallelize(List(("jerry",2),("tom",2),("dog",10)))

    //合并数据
    val rdd15_3 = rdd15_1 union rdd15_2
    //聚合
    val rdd15_4: RDD[(String, Int)] = rdd15_3.reduceByKey(_+_)

    //排序 sortBy
    //现在数据key是String类型,后面Int类型进行排序
    val rdd15_5: RDD[(Int, String)] = rdd15_4.map(t => (t._2,t._1))
    val rdd15_6: RDD[(Int, String)] = rdd15_5.sortByKey(false)
    val rdd15_7: RDD[(String, Int)] = rdd15_6.map(t => (t._2,t._1))
    println(rdd15_7.collect.toBuffer)

    //笛卡尔积
    val rdd16: RDD[((String, Int), (String, Int))] = rdd15_1 cartesian rdd15_2

    val rdd17 = sc.parallelize(List(2,5,1,62,7,3,267))
    println(rdd17.count())//数据个数
    println(rdd17.top(3).toBuffer)//取值 默认会降序排序,若输入0,会返回一个空数组
    println(rdd17.take(3).toBuffer)//取值,去除对应数量的数值
    println(rdd17.takeOrdered(3).toBuffer)//取值会进行排序,默认是升序,返回对等数量的数据
    println(rdd17.first())//取出第一个值
  }
}

详细说明groupByKey和reduceByKey

1.groupByKey:groupBykey会对每一个RDD中的value值进行聚合形成一个序列,此操作发生在reduce端之前,所以势必会将所有的数据通过网络传输,造成不必要的浪费 数据量十分巨大,可以造成OOM

2.reduceByKey: 会在结果发送给reduce之前进行一次合并计算,相同key所有的value相加,这个操作类似于MR中的Combiner,这样做的好处在于,map端会进行一次reduce,会减少数据量,从而减少传输,可以保证后期reduce更高效的计算。

在这里插入图片描述
建议:在大量数据进行计算的时候建议使用reduceByKey,不建议用groupByKey

高阶算子

1.PlusOperatorDemo1.scala

/**
  * 高阶算子
  */
object PlusOperatorDemo1 {
  //自定义打印方法
  def printlns[T](rdd:RDD[T]):Unit = {
    println(rdd.collect.toBuffer)
  }
  def fun1[T](index:Int,iter:Iterator[(T)]): Iterator[String]={
    iter.map(x  => "[partID:  "+index+" value:  "+x+"]")
  }
  def main(args: Array[String]): Unit = {
    val conf = new SparkConf().setAppName("PlusOperatorDemo1").setMaster("local")
    val sc = new SparkContext(conf)
    //1.遍历出集合中的每一个元素
    //并行化生成RDD
    val rdd1: RDD[Int] = sc.parallelize(List(1,2,3,4,5,6),3)
    val rdd2: RDD[Int] = rdd1.map(_ * 10)
    printlns(rdd2) //ArrayBuffer(10, 20, 30, 40, 50, 60)
    //mapPartion是对每个分区中的数据进行迭代
    /**
      * 第一个参数是第一个迭代器对象,
      * 第二个参数表示是否保留父RDD的partition分区信息
      * 第二个参数的默认值是: false ,一般是不修改(父RDD就需要用到宽窄依赖的问题)
      * (f: Iterator[T] => Iterator[U],preserve Partitioning:Boolean = false)
      * ps:第一个 _ 是迭代器对象,第二个_ 是分区(集合)中数据
      * map和mapPartitions的区别:
      * map是对RDD中的每个元素进行操作
      * mapPartitions则是对RDD中的每个分区中的迭代器进行操作
      */
      val rdd3: RDD[Int] = rdd1.mapPartitions(_.map(_ * 10))

    /**
      *如果RDD数量不大,建议采用mapPartitions算子代替map算子,可以加快数据量的处理数据
      * 但是如果RDD中数据量过大比如10亿条,不建议使用apPartitions今次那个数据遍历,可能出现OOM,内存溢出错误
      *
      */

    //mapWith:是对RDD中每个元素进行操作(是map方法的另外一种方法)
    //计算每一个分区中的值
    //(constructA: Int) => A,preservesPartitioning: Boolean = false)(f:(T,A)=>U)
    //(1,2,3,4,5,6)
    //参数是柯里化
    /**
      * 第一个参数是分区号(分区号是从0开始的)
      * 第二个参数是否保留父RDD的partition分析信息
      * 第二个参数T分区中的每一个元素 A是第一个柯里化参数中第一个参数得到的结果
      * 柯里化第一个参数是分区号逻辑  柯里化第二个参数实际对应分区中元素的处理逻辑
      * 这个方法已经过时
      */
    println(rdd1.mapPartitionsWithIndex(fun1).collect().toBuffer)
    val rdd4: RDD[Int] = rdd1.mapWith(i =>
      {
        println(i)
        i * 10
      })((a, b) => {
      println("a = "+a)
      //(constructA: Int) => A,preservesPartitioning: Boolean = false)(f:(T,A)=>U)
      println("b = "+b) //其实是第一个柯里化参数处理的结果,b = i * 10---b 就是A
      println("a + b + 2 ="+(a+b +2))
      println("-------------------------------------")
      a+b+2
    })
    printlns(rdd4) //ArrayBuffer(3, 4, 15, 16, 27, 28)
    /***结果:
      * ArrayBuffer([partID:  0 value:  1], [partID:  0 value:  2], [partID:  1 value:  3], [partID:  1 value:  4], [partID:  2 value:  5], [partID:  2 value:  6])
      * 0
      * a = 1
      * b = 0
      * a + b + 2 =3
      * -------------------------------------
      * a = 2
      * b = 0
      * a + b + 2 =4
      * -------------------------------------
      * 1
      * a = 3
      * b = 10
      * a + b + 2 =15
      * -------------------------------------
      * a = 4
      * b = 10
      * a + b + 2 =16
      * -------------------------------------
      * 2
      * a = 5
      * b = 20
      * a + b + 2 =27
      * -------------------------------------
      * a = 6
      * b = 20
      * a + b + 2 =28
      * -------------------------------------
      * ArrayBuffer(3, 4, 15, 16, 27, 28)
      */

    /**
      * flatMap是对rdd中每个元素进行操作,返回的结果是一个扁平化处理过后的
      * (constructA: Int => A, preservesPartitioning: Boolean = false)(f: (T, A) => Seq[U])
      * 参数使用和上面是一样的,只不过最后的返回值还是一个序列
      * 这个方法也过时
      *
      */
      println("------------------------rdd5-----------------------------")
    println(rdd1.mapPartitionsWithIndex(fun1).collect().toBuffer)
    val rdd5: RDD[Int] = rdd1.flatMapWith(i =>{
      println("i = "+i)
      i
    })((x, y) => {
      println("x = "+x)
      println("y = "+ y)
      println("-------------------------------------")
      List(y,x)
    })
    rdd1.flatMapWith(i => i)((x,y) =>List(y,x))
     printlns(rdd5)

    /**
      * 结果:
      * i = 0
      * x = 1
      * y = 0
      * -------------------------------------
      * x = 2
      * y = 0
      * -------------------------------------
      * i = 1
      * x = 3
      * y = 1
      * -------------------------------------
      * x = 4
      * y = 1
      * -------------------------------------
      * i = 2
      * x = 5
      * y = 2
      * -------------------------------------
      * x = 6
      * y = 2
      * -------------------------------------
      */

    /**
      * mapPartitionsWithIndex 是对rdd中每个分区的遍历操作
      * 第一个参数是一个柯里化
      * 第二参数是一个隐式转换
      *  函数的作用和mapPartitions是类似的,不过要提供两个采纳数,
      *  第一个参数是分区号
      *  第二个参数是分区中元素存储到Iterator中(就可以操作这个Iterator)
      *  第三个参数是 是否保留RDD的partition
      */

      val func = (index:Int,iter:Iterator[(Int)]) => {
        iter.map(x => "[partID: "+index+",value: "+x+ "]")
      }
    val rdd6: RDD[String] = rdd1.mapPartitionsWithIndex(func)
    println(rdd6.collect().toBuffer)
//结果:ArrayBuffer([partID: 0,value: 1], [partID: 0,value: 2], [partID: 1,value: 3], [partID: 1,value: 4], [partID: 2,value: 5], [partID: 2,value: 6])

    /**
      * Aggregate算子:聚合
      * Aggregate函数将每个分区里面的元素进行聚合,然后用ombine函数将每个分区的结果和初始值进行combine操作
      * ps:combine-->执行reduce,这个函数的最终返回值类型不要和RDD中元素的类型一致
      * zeroValue--初始值(默认值) seqOP局部聚合(分区的),comOP(全局聚合)
      * (zeroValue: U) (seqOP:(U,T)) (comOP:(U,U) => U)
      */

     val  rdd7 = sc.parallelize(List(1,2,3,4,5,6,7,8,9),2)
    println(rdd7.mapPartitionsWithIndex(fun1).collect().toBuffer)

    /**
      * zeroValue是初始值
      * 初始值会先和分区中的数据进行比较计算,最终全局聚合的时候,每个分区相加,然后再加上默认值
      * ps:初始值会跟分区进行局部聚合(分区计算)
      * 初始值会跟全局聚合在一起进行计算
      * aggregate是action类型的算子
      * 初始值会参与运算,刚开始选最大值的时候参与,到开始聚合运算的时候也参与
      */
     val sumed: Int = rdd1.aggregate(0)(math.max(_,_),_+_)
  }
}

2.AggregateDemo.scala

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

/**
  * Aggregate算子:聚合
  * Aggregate函数将每个分区里面的元素进行聚合,然后用combine函数将每个分区的结果和初始值进行combine操作
  * ps:combine--->执行以下reduce,这个函数最终的返回值类型不要和RDD中元素的类型一致
  * zoroValue是初始值(默认值)  seqOP局部聚合(分区的),comOP全局聚合
  * (zoroValue : U) (seqOP :(U, T)) (comOP:(U,U)=>U)
  */
object AggregateDemo {
  def fun1[T](index:Int,iter:Iterator[(T)]): Iterator[String]={
    iter.map(x  => "[partID:  "+index+" value:  "+x+"]")
  }
  def main(args: Array[String]): Unit = {
    val conf = new SparkConf().setAppName("AggregateDemo").setMaster("local[2]")
    val sc = new SparkContext(conf)
    val  rdd1 = sc.parallelize(List(1,2,3,4,5,5,6,7,8,9),2)
    println(rdd1.mapPartitionsWithIndex(fun1).collect().toBuffer)
    //zeroValue是初始值(默认值)
    /**
      * 初始值会先和分区中的数据进行比较计算,最终全局聚合的时候每个分区值相加然后再加上默认值
      * ps:初始值会跟分区进行局部聚合(分区计算)
      * 初始值会跟全局聚合在一起进行计算
      * aggregate是action类型的算子
      * 初始值会参与运算,刚开始选最大值的时候参与,到开始聚合运算的时候也参与
      */
    val sumed = rdd1.aggregate(0)(math.max(_,_),_+_)
    println(sumed)//14
    //(math.max(_,_),_+_)
    /**
      * 初始值也参加运算--1 2 3 4 5 | 5 6 7 8 9
      * 局部聚合的时候参与了运算,全局操作也参与计算
      * math.max(_,_)
      * 刚开始第一个 _ 代表初始值,
      * 第二个 _ 代表第一个分区里面的第一个元素
      * 然后进行比较,得到最大值
      * 并将最大值放在第一个_那里面,
      * 分区中的下一个元素放在 第二个下划线
      * ,_+_)
      * 将在每个分区中取到的最大值进行聚合
    */
    val sumed2 = rdd1.aggregate(5)(math.max(_,_),_+_)
    println(sumed2)//5 + 5 +9 = 19

    //abcdef 或者defabc  为什么?
    /**
      * aggregare是先计算分区的值,并行化处理的情况
      * 两个分区可能出现两个线程在跑,哪个分区先完成不一定,所以就会出现
      * 谁先执行完,谁就在前面,剩下的在后面
      */
    val rdd2: RDD[String] = sc.parallelize(List("a","b","c","d","e","f"),2)
    println(rdd2.mapPartitionsWithIndex(fun1).collect().toBuffer)
    //初始值先分别和分区中的元素进行聚合运算,然后再全局聚合的时候,又再次参与聚合运算
    val str: String = rdd2.aggregate("")(_+_,_+_)
    println(str)
    /**
      * 初始值在进行局部聚合的时候,会和分区中的值进行一次计算,
      * 所有分区计算完成后,会在全局聚合的时候再子进行一次计算
      */
    val str1: String = rdd2.aggregate("=")(_+_,_+_)
    println(str1)
    val rdd3: RDD[String] = sc.parallelize(List("12","34","345","4567"),2)
    println(rdd3.mapPartitionsWithIndex(fun1).collect().toBuffer)

    val str2: String = rdd3.aggregate("")((x, y) => math.max(x.length,y.length).toString, (x, y)=>x+y)
     println(str2) //24  42

    val rdd4: RDD[String] = sc.parallelize(List("12","34","345",""),2)
    println(rdd4.mapPartitionsWithIndex(fun1).collect().toBuffer)

    val str3: String = rdd4.aggregate("")((x, y) => math.min(x.length,y.length).toString, (x, y)=>x+y)
    println(str3)

    println("".length.toString)//0
    println("".length.toString.length)//1

    val rdd5: RDD[String] = sc.parallelize(List("12","34","","345"),2)
    println(rdd5.mapPartitionsWithIndex(fun1).collect().toBuffer)

    val str4: String = rdd5.aggregate("")((x, y) => math.min(x.length,y.length).toString, (x, y)=>x+y)
    println(str4)

    //AggregateByKey
    //相同key中的值进行聚合操作,通过aggregateByKey函数最终返回的类型还是RDD(PairRDD)
    val rdd6: RDD[(String, Int)] = sc.parallelize(List(("cat",2),("cat",5),("dog",4),("dog",3),("pig",10),("cat",5)),2)
    println("-------------------------------------------")
    println(rdd6.mapPartitionsWithIndex(fun1).collect().toBuffer)
    //AggregateNyKey对应的是二元组的计算,使用方式和Aggregate没有太大区别
    //初始值 分区聚合(局部) 全局聚合  比较最大值,然后用最大值求和
    //先计算对应分区的值,然后再全局聚合
    //ps:因为第一次给的数值每个分区中是没有相同key,所有都是最大值,所有就相当于值相加了
    //第二次将同一个分区中的key有相同
    //首先会根据相同key进行计算,以cat为例子先会和初始值计算,比较留下最大值
    //然后等待第二分区完成计算,然后再进行全局聚合
    val value: RDD[(String, Int)] = rdd6.aggregateByKey(0)(math.max(_,_),_+_)
    println(value.collect().toBuffer)
    }
}

3.combineByKey.scala

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

//根据相同key进行聚合
//createCombiner: V => C,
//      mergeValue: (C, V) => C,
//      mergeCombiners: (C, C) => C)
/**
  * 第一个参数是:遍历集合中value的值,V 和 C 的数据类型:由value的数据类型决定
  * 相同与根据key生成一个类型 key [多value]的集合 hello[1,1,1,1,1,1,1,1,1,1]
  *
  * 第二个参数  局部聚合
  * c 的数据类型时第一个参数返回值决定
  * v 的数据类型时第一个参数的,数据类型决定
  * ps: 若 key [多个value]集合,进行局部聚合
  *
  * 第三个参数  全局聚合
  * 对第二个函数中每个分区操作产生的结果 再次进行聚合
  * C 的数据类型时第二个函数得到的数据类型 最终聚合
  *
  */
object combineByKey {
  def main(args: Array[String]): Unit = {
    val conf = new SparkConf().setAppName("AggregateDemo").setMaster("local")
    val sc = new SparkContext(conf)
    //计算单词的个数:reduceByKey,groupByKey,aggregateByKey,combineBykey
    val rdd1: RDD[(String, Int)] = sc.textFile("dir/file.txt").flatMap(_.split(" ")).map((_,1))
    //下划线不行,就写变量,写变量不行就加数据类型
    //x :当前去的所有值
    val rdd2: RDD[(String, Int)] = rdd1.combineByKey(x => x, (a:Int, b:Int)=>{
     println("a = "+a)
      println("b = "+b)
      println("a+b = "+(a+b))
      println("--------------------")
      a+b
    }, (m:Int, n:Int)=>{
      println("m = "+m)
      println("n = "+n)
      println("m+n = "+(m+n))
      println("------------------------")
      m+n
    })
    println(rdd2.collect().toBuffer)

    val rdd3: RDD[(String, Int)] = rdd1.combineByKey(x => x+10, (a:Int, b:Int)=>a+b, (m:Int, n:Int)=> m+n)
    println(rdd3.collect().toBuffer)

    val rdd4: RDD[String] = sc.parallelize(List("tom","jerry","kitty","cat","dog","pig","bird","bee","wolf"),3)
    val rdd5: RDD[Int] = sc.parallelize(List(1,1,2,2,2,1,2,2,2),3)
    val rdd6:RDD[(Int,String)]= rdd5.zip(rdd4)
    //需求key 是1的放在一起,key是2的放在一起
    //第一个参数是获取value的值
    val rdd7= rdd6.combineByKey(List(_),(x:List[String],y:String)=>x :+ y,(m:List[String],n:List[String])=> m++ n)
    println(rdd7.collect().toBuffer)
  }

}

4.OtherOperaterdDemo.scala

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

object OtherOperaterdDemo {
  def fun1[T](index:Int,iter:Iterator[(T)]): Iterator[String]={
    iter.map(x  => "[partID:  "+index+" value:  "+x+"]")
  }
  def main(args: Array[String]): Unit = {
    //countByKey 属于action类型的算子 统计key的个数
    val conf = new SparkConf().setAppName("OtherOperaterdDemo").setMaster("local[2]")
    val sc = new SparkContext(conf)
    val rdd1: RDD[(String, Int)] = sc.parallelize(List(("a",1),("b",1),("a",1)))
    val key: collection.Map[String, Long] = rdd1.countByKey()
    println(key)
    val value: collection.Map[(String, Int), Long] = rdd1.countByValue()
    //统计value的个数,但是会将集合中得到的一个元素看做是一个value
    println(value)

    //filterByRange: 对RDD中的元素进行过滤,返回指定范围内的数据
    val rdd2: RDD[(String, Int)] = sc.parallelize(List(("e",5),("c",3),("d",4),("c",2),("a",1)))
    val rdd2_1: RDD[(String, Int)] = rdd2.filterByRange("c","e")
    println(rdd2_1.collect().toList)

    //flatMapValues对参数进行扁平化操作,是value的值
    val rdd3: RDD[(String, String)] = sc.parallelize(List(("a","1 2"),("b","3 4")))
    println(rdd3.flatMapValues(_.split(" ")).collect().toList)

    //foldByKey根据相同key进行聚合
    val rdd4: RDD[(String, Int)] = sc.textFile("dir/file.txt").flatMap(_.split(" ")).map((_,1)).foldByKey(0)(_+_)
    println(rdd4.collect().toBuffer)

    //foreachartition 循环的是分区数据
    //3个分区 一个3个数
    val rdd5 = sc.parallelize(List(1,2,3,4,5,6,7,8,9),3)
    rdd5.foreachPartition(x => println(x.toList))
    rdd5.foreachPartition(x =>println(x.reduce(_+_)))// 6 15 24

    //foreachPartition 一般应用于数据的持久化,存入 数据库,可以进行分区的数据存储

    //KeyBy 以传入的函数返回值作为key RDD 中的元素为Value元素的元组
    val rdd6: RDD[String] = sc.parallelize(List("dog","cat","pig","wolf","bee"),3)
    val rdd6_1: RDD[(Int, String)] = rdd6.keyBy(_.length)
    println(rdd6_1.collect.toList)

    //Keys获取所有的key ,values 获取所有的value
    println(rdd6_1.keys.collect().toList)
    println(rdd6_1.values.collect().toList)

    //collectAsMap 将需要的二元组成Map
    val map: collection.Map[String, Int] = rdd2.collectAsMap()
    println(map)//去重


    //重新分区算子:  repatition coaesce  partitionBy
    val rdd7: RDD[Int] = sc.parallelize(1 to 10,4)
    println(rdd7.partitions.length)
   println(rdd7.mapPartitionsWithIndex(fun1).collect().toList)
    //重新分区的时候,数据会进行Shuffle
    //重新分区
    val rdd7_1: RDD[Int] = rdd7.repartition(6)
    println(rdd7_1.partitions.length)
   //第二 参数是shuffle,默认不是false
    //当前分区数大于原有分区数,若不Shuffle,不会进行修改,只有改变为true才会
    //当前分区数小于原有分区数会直接分区,false不shuffle,true 可以shuffle
    val rdd7_2 = rdd7.coalesce(6,true)
    println(rdd7_2.partitions.length)

    //partitionBy 必须是KV数据类型
    val rdd7_3 = sc.parallelize(List(("e",5),("c",3),("d",4),("c",2),("a",1)))
    //可以传入自定义分区器,也可以传入默认分区器 HashPartitioner
    val rdd7_4: RDD[(String, Int)] = rdd7_3.partitionBy(new HashPartitioner(4))
    println(rdd7_4.partitions.length)

    //Checkpoint
    /**
      * 检查点,类似于快照,checkpoint的作用就是将DAG中的数据中比较重要的数据做一个检查点,将结果存储到一个高可用的地方
      *
      */
    //1.指定存储目录
    sc.setCheckpointDir("hdfs://hadoop01:8020/ck")
    //检查点的触发一定要使用action类型的算子
    val rdd8: RDD[(String, Int)] = sc.textFile("hdfs://hadoop1:8020/word.txt").flatMap(_.split(" ")).map((_,1)).reduceByKey(_+_)
    //检查点的触发一定要使用这个action算子
    rdd8.checkpoint()
    rdd8.saveAsTextFile("hdfs://hadoop1:8020/out10")
    println(rdd8.getCheckpointFile)//查看存储的位置

    //查看是否可以设置检查点,rdd8.isCheckpointinted
  }
}

猜你喜欢

转载自blog.csdn.net/qq_35078688/article/details/84800951
今日推荐