Transformation 的各种算子详解

RDD是一个弹性分布式可复原的数据集,他是一个抽象的他里面并不装数据,他里面装的是,描述以后从哪里读数据描述以后怎么算,对RDD进行操作本质上是对RDD里面的每一个分区进行操作,一个分区对应一个迭代器,其实是吧算子传到迭代器里面了,对RDD进行map本质上是对里面的每一个分区,对应的数据进行map,其实解释调用迭代器的方法,然后被函数传进去,但是我没在实际计算中有可能传很多的逻辑在一个stage(阶段)里面其实是形成了迭代器链,对数据进行了链式操作,(注意是在一个stage里面),因为以后会有suffer,会切分stage,在每个stage里面最后一个stage都是往外拉数据的

mapPartitionsWithIndex算子的代码详解

package cn._51doit.demo01.Word

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

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

    //创建SparkContext,只有使用SparkContext才可以向集群申请资源,才可以创建RDD
    val conuts = new SparkConf().setAppName("Conuts").setMaster("local[*]")
    val sc = new SparkContext(conuts)

    val arr=Array(1,2,3,4,5,6,7,8,9,10)
    //如果通过paalleilize方法创建RDD会把数据分在分区里更加的均匀
    val rdd1= sc.parallelize(arr,3)
//以元素为单位进行map
//    rdd1.map(i =>{
//      //数据是一条一条的作为函数的参数输入,每输入一条都有返回
//      i * 100
//    })
    //对Partition进行map操作.与此同时可以将分区标号获取到.输入的是迭代器返回的也是迭代器
    //以分区为单位进行map
    //注:Partition是分区,index是分区编号
       val rdd2 = rdd1.mapPartitionsWithIndex((index, iter) => {
       val nIter = iter.map(_ * 100)
  //   val nIter = iter.map(i => s"partition: $index, value: $i")
       nIter
       })
    val ints = rdd2.collect()
    println(ints.toBuffer)
    //不退出线程,让他多睡一会
    Thread.sleep(1000000000)
  }
}

mapPartions算子详解

package cn._51doit.demo01.Word

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

object MapPartitionsDemo {
  def main(args: Array[String]): Unit = {
    //创建SparkContext,只有使用SparkContext才可以向集群申请资源,才可以创建RDD
    val conuts = new SparkConf().setAppName("Conuts").setMaster("local[*]")
    val sc = new SparkContext(conuts)

    val arr=Array(1,2,3,4,5,6,7,8,9,10)
    //如果通过paalleilize方法创建RDD会把数据分在分区里更加的均匀
    val rdd1= sc.parallelize(arr,3)
    //以元素为单位进行map
    //    rdd1.map(i =>{
    //      //数据是一条一条的作为函数的参数输入,每输入一条都有返回
    //      i * 100
    //    })

    //使用mapPartitions实现过滤
    val rdd2 = rdd1.mapPartitions(it => {
      it.filter(_ %2 ==0)
    })
    val ints = rdd2.collect()
    println(ints.toBuffer)

    Thread.sleep(10000000)
  }
}

下面是加了个从别的地方传过来的链接解释一下用哪一个好

package org.apache.spark.day02

import java.sql.{DriverManager, ResultSet}

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

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

    //创建SparkContext,只有使用SparkContext才可以向集群申请资源,才可以创建RDD
    val conuts = new SparkConf().setAppName("Conuts").setMaster("local[*]")
    val sc = new SparkContext(conuts)

    val arr = Array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
    //如果通过paalleilize方法创建RDD会把数据分在分区里更加的均匀
    val rdd1 = sc.parallelize(arr, 3)
    //以元素为单位进行map
        rdd1.map(i =>{
          //链接数据库的语句,效率低   参数1是数据库路径,参数2是用户名,参数三是密码
          val connection = DriverManager.getConnection("", "", "")
            //创建pstm 括号里是sql语句提取要的数据
          val pstm = connection.prepareStatement("SELECT name from tb_catgory WHERE id = ?")
          pstm.setInt(1,i)
          val rs: ResultSet = pstm.executeQuery()
          var name =null
          if(rs.next()){
            name=rs.getString(1)
          }
          //关闭资源
          rs.close()
          pstm.close()
          connection.close()
        })
    //使用mapPartitions实现过滤
      val rdd2 = rdd1.mapPartitions(it => {
        //链接数据库的语句  参数1是数据库路径,参数2是用户名,参数三是密码
        val connection = DriverManager.getConnection("", "", "")
        //创建pstm 括号里是sql语句提取要的数据
        val patm = connection.prepareStatement("SELECT name from tb_category WHERE id = ?")
        val niter =it.map(i => {
          patm.setInt(1,i)
          val rs: ResultSet = patm.executeQuery()
          var name = null
          if(rs.next()){
            name = rs.getString(1)
          }
          rs.close()
          (i,name)
        })
        patm.close()
        connection.close()

      })


    //因为MapPartitionsRDD必须在spark包及其子包下使用所以要伪装一下
    //对于窄依赖的操作(没有suffer)可以直接new
    //中括号里面写输入的数据类型和返回的数据类型,返回的在前面
//    val rdd2 = new MapPartitionsRDD[String, Int](rdd1, (tc, index, it) => {
//      //tc也可以拿到mapPartitionsId,tc.partitionsId,就和index一样,但是他还可以点更多的方法
//      it.map(i =>s"partition:${tc.partitionId()},value:$i")
//    })
//    val ints = rdd2.collect()
//    println(ints.toBuffer)
  }
}

写了一个从mysql读文件的案例

这个创建的mysql的路径不能放在外面是因为要先序列化task传到别的机器上运行所以序列化的端口号之类的都变了,只能放里面但是放里面如果用map方法就要用一次调一次这样不好,所以用mapPartitions比较好,因为可以写再方法里面和自己定义的函数外面,关闭也要再方法里面判断最后一条数据再关闭,应为代码走到最后参会运行,这时候上面已经关闭了

package org.apache.spark.day02

import java.sql.{DriverManager, ResultSet}

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

object QueryCategoryNameFromMySQL {
  def main(args: Array[String]): Unit = {
    //设置为本地模式.setMaster("local[*]")
    val conf = new SparkConf().setAppName("QueryCategoryNameFromMySQL").setMaster("local[*]")
    val sc = new SparkContext(conf)

    val arr= Array(1,2,3,4,2,6,4,2,3,4,5,6,1)
    val rdd1: RDD[Int] = sc.parallelize(arr, 2)

    //不能再Driver端创建,会导致Task无法序列化
    //val connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/doit_19?characterEncoding=utf8", "root", "123456")

    val rdd2 =rdd1.mapPartitions(it => {
      //这边就是写连接mysql的路径,用户名,密码
      val connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/doit_19?characterEncoding=utf8", "root", "123456")
      //这边是sql语句提取白哦个里数据的,同样也确定了表名
      val pstm = connection.prepareStatement("SELECT name from tb_sp WHERE id=?")
      //下面就是自己的函数
      it.map(i => {
        pstm.setInt(1,i)
        val rs: ResultSet = pstm.executeQuery()
        var name="null"
        if(rs.next()){
         name =  rs.getString(1)
        }
        //关闭ResultSet
        rs.close()
        //处理完分区的最后一条数据.将连接关闭
        if(!it.hasNext){
          pstm.close()
          connection.close()
        }
        (i,name)
      })

    })
    val res =rdd2.collect()
    println(res.toBuffer)
  }
}

总节上面所说的几个方法,map,mapPartitions和mapRartitionsWithIndex这三个方法
map是将数据一条一条的拿出来本质上是调迭代器的map方法把数据一条一条的传入了你写的函数里面,而mapPartitions和mapRartitionsWithIndex本质上是吧迭代器传入到你的函数里面,你的函数里面就可以直接获取到一个迭代器,以后迭代器会一条一条的把数据遍历出来,所以以后要是想查库什么的,可以把连接事先创建好以后一个数据里面的多条数据 就都可以用这个连接对象
在这里插入图片描述

mapValues的使用和源码分析

mapValues就是对k,v的v进行操作返回k和处理过的v
val value = rdd1.mapValues(i => i * 10)

package cn._51doit.demo01.Word

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

object KeysValuesDemo {
  def main(args: Array[String]): Unit = {
   //设置为本地模式
   val conf = new SparkConf().setAppName("KeysValuesDemo").setMaster("local[*]")
    val sc = new SparkContext(conf)

    val arr = Array(("spark", 2), ("hadoop", 3), ("flink", 4), ("spark", 5))
    val rdd1: RDD[(String, Int)] = sc.parallelize(arr, 3)
//这时候就得到了所有的key,是String类型的
    //val keys = rdd1.map(_._1)
    //val keys1 = rdd1.keys
    val keys: RDD[String] = new PairRDDFunctions[String, Int](rdd1).keys
    //上面这三个方法都是一样的
    println(keys.collect().toBuffer)
  }
}

flatMapValues的使用和源码分析

就是跟上面一样只对v进行操作多了一个事先亚平的操作

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

object FlatMapValues {
  def main(args: Array[String]): Unit = {
    //设置为本地模式
    val conf = new SparkConf().setAppName("KeysValuesDemo").setMaster("local[*]")
    val sc = new SparkContext(conf)

    val arr = Array(("spark", "1,2,3"), ("hadoop", "4,5,6"), ("flink", "4,5,6"), ("spark", "1,2,3"))
    val rdd1 = sc.parallelize(arr, 3)

//    val rdd2 = rdd1.flatMap(i => {
//      //把k字符串提取出来,把v字符串再提取出来切割,用map方法每一个元素和拼在一起
//      val key = i._1
//      val value: Array[String] = i._2.split(",")
//      val arr = value.map(i => (key, i.toInt))
//      arr
//    })
    //这是调用了flatmapvalues先亚平再对v进行操作,
val rdd2: RDD[(String, Int)] = rdd1.flatMapValues(_.split(",").map(_.toInt))
    println(rdd2.collect().toBuffer)
  }
}
ArrayBuffer((spark,1), (spark,2), (spark,3), (hadoop,4), (hadoop,5), (hadoop,6), (flink,4), (flink,5), (flink,6), (spark,1), (spark,2), (spark,3))

union算子的使用和源码分析

就是两个RDD合并,但是本质上是生成了一个新的RDD把原来的两个RDD包起来了,分区数量是原来的两个分区数量的和而且数据该再哪台机器上运行还再哪台机器上运行,为什么要将两个RDD搞一起嫩,因为以后好事对两个RDD进行相同的map操作就比较麻烦,就搞一起再map就方便一点

package cn._51doit.demo01.Word

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

object UnionDemo {
  def main(args: Array[String]): Unit = {
    //设置为本地模式
    val conf = new SparkConf().setAppName("KeysValuesDemo").setMaster("local[*]")
    val sc = new SparkContext(conf)

    val arr1=Array(1,2,3,4,5)
    val rdd1 = sc.parallelize(arr1, 3)

    val arr2=Array(4,5,6,7,8,9)
    val rdd2 = sc.parallelize(arr2, 3)
    //只能union类型相同的,不会去重,没有suffer
    val rdd3 = rdd1.union(rdd2)
    println(rdd3.collect().toBuffer)
  }
}

aggregateBykey的使用和源码分析

注意:有suffer的算子再满足一种特殊的条件之上是不用suffer的,比如事先已经分好区了,以后分的区使用相同的分区器并且分区数量没有改变就不会suffer

package cn._51doit.demo01.Word

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

object AggregateByKeyDemo {
  def main(args: Array[String]): Unit = {
    //设置为本地模式.setMaster("local[*]")
    val conf = new SparkConf().setAppName("WordCount").setMaster("local[*]")
    val sc = new SparkContext(conf)
//    val arr: Array[(String, String)] = Array(("spark", "1,2,3"), ("hadoop","2,3,4"), ("flink", "3"), ("spark", "5,6"))
//    val rdd1: RDD[(String, String)] = sc.parallelize(arr, 3)
//    val rdd2: RDD[(String, Int)] = rdd1.flatMapValues(_.split(",").map(_.toInt))
//    //就是先局部聚合再全局聚合,把k相同v加一起,0是一个初始值,前面的_+_局部聚合和全局聚合的相加,后面的_+_不用管
//    val rdd3: RDD[(String, Int)] = rdd2.aggregateByKey(0)(_ + _, _ + _)
//    println(rdd3.collect().toBuffer)

    val lst = List(("cat",2), ("cat", 5), ("mouse", 4),("mouse", 3),
      ("cat", 12), ("dog", 12), ("dog", 15), ("mouse", 7),("mouse", 2))
    //这里就分了两个分区因为用的是parallelize所以一个是4个元素一个是5个元素
    val pairRDD = sc.parallelize(lst, 2)
    //调用了一个max方法是将两个分区中最大的一个提取出来再聚合得到(dog,15), (cat,17), (mouse,11)
    //val rdd2 = pairRDD.aggregateByKey(0)(Math.max(_, _), _ + _)
    //这样的结果是(dog,127), (cat,219), (mouse,216),代表初始值再每个分区的每个相同k第一次出现会加上初始值,所以dog只能加一个100
    val rdd2 = pairRDD.aggregateByKey(100)(_+_ , _ + _)
    println(rdd2.collect().toBuffer)
  }
}

foldByKeyDemo的使用和源码分析

他的用法也就是把k形同的v聚合相加初始值局部聚合使用一次

package cn._51doit.day03

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

object FoldByKeyDemo {

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

    //设置为本地模式.setMaster("local[*]")
    val conf = new SparkConf().setAppName("WordCount").setMaster("local[*]")
    val sc = new SparkContext(conf)

    val lst = List(("cat",2), ("cat", 5), ("mouse", 4),("mouse", 3),
      ("cat", 12), ("dog", 12), ("dog", 15), ("mouse", 7),("mouse", 2))
    val pairRDD = sc.parallelize(lst, 2)

    val rdd2 = pairRDD.foldByKey(0)(_ + _)

    println(rdd2.collect().toBuffer)

    Thread.sleep(100000000)
  }
}

suffle算子的不suffle的特殊情况

package cn._51doit.demo01.Word

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

object NoSuffleDemo {
  def main(args: Array[String]): Unit = {
    //设置为本地模式.setMaster("local[*]")
    val conf = new SparkConf().setAppName("WordCount").setMaster("local[*]")
    val sc: SparkContext = new SparkContext(conf)

    val lst = List(("cat",2), ("cat", 5), ("mouse", 4),("mouse", 3),
      ("cat", 12), ("dog", 12), ("dog", 15), ("mouse", 7),("mouse", 2))
    val pairRDD = sc.parallelize(lst, 2)

    //只按照指定的分区器进行分区,不聚合,不分组,这边也是指定和前面分区一样了不会suffle不会产生stage
    val rdd2 = pairRDD.partitionBy(new HashPartitioner(pairRDD.partitions.length))
    //这边指定分区和前面一样了所以不会suffle不会产生stage
    //val rdd2 = new ShuffledRDD[String, Int, Null](pairRDD, new HashPartitioner(pairRDD.partitions.length))
    //再这的时候重新指定了一次分区会suffle会产生stage
    val reduced = rdd2.reduceByKey(new HashPartitioner(3), _ + _)
      //最后结束了还会suffle一次产生stage
    reduced.saveAsTextFile("")
  }
}

cogroup的用法

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

//协分组,联合分组,是对多个RDD进行分组
//RDD必须是KeyValue类型的,并且两个RDD的key类型一样才能进行cogroup
object CogroupDemo {

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

    val conf = new SparkConf().setAppName("WordCount").setMaster("local[*]")
    val sc = new SparkContext(conf)

    val rdd1 = sc.parallelize(List(("tom", 1), ("tom", 2), ("jerry", 3), ("kitty", 2)))
    val rdd2 = sc.parallelize(List(("jerry", 2), ("tom", 1), ("shuke", 2)))

    val rdd3 = sc.parallelize(List(("jerry", 2.3), ("tom", 1.1), ("shuke", 2.7)))

    //val rdd: RDD[(String, (Iterable[Int], Iterable[Int]))] = rdd1.cogroup(rdd2)

    val rdd: RDD[(String, (Iterable[Int], Iterable[Int], Iterable[Double]))] = rdd1.cogroup(rdd2, rdd3)
    println(rdd.collect().toBuffer)
  }
}
ArrayBuffer((tom,(CompactBuffer(1, 2),CompactBuffer(1),CompactBuffer(1.1))), (shuke,(CompactBuffer(),CompactBuffer(2),CompactBuffer(2.7))), (kitty,(CompactBuffer(2),CompactBuffer(),CompactBuffer())), (jerry,(CompactBuffer(3),CompactBuffer(2),CompactBuffer(2.3))))
最后生成的结果是这样的,生成了一个集合,集合里面装的是好多对偶元组,元素一是单词,元素二是元组,元组里元素是联合分组的RDD,一个RDD就是一个元素,元素是集合类型的装的是k相同的v

join算子的使用和源码分析

join就是两个RDD进行连接,k相同的会连接到一起,join的前提是k的类型一样,并且以后k的值是等值的才会连到一起,其实他底层调用的就是cogroup
说白了就是第一个RDD跟第二个有相同k的组合

package cn._51doit.demo01.Word

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

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

    val rdd1 = sc.parallelize(List(("tom", 1), ("tom", 2), ("jerry", 3), ("kitty", 2)))
    val rdd2 = sc.parallelize(List(("jerry", 2), ("tom", 1), ("shuke", 2)))

    //实现innerJoin这是直接用join方法,相当于rdd1和rdd2里有rdd1的k做匹配,结果是(tom,(1,1)), (tom,(2,1)), (jerry,(3,2))
    //val rdd3: RDD[(String, (Int, Int))] = rdd1 join rdd2

    //使用cogroup实现
    val rdd3: RDD[(String, (Iterable[Int], Iterable[Int]))] = rdd1.cogroup(rdd2)
    //这是普通方法
    //经过上面个的cogroup变成了((tom,(CompactBuffer(1, 2),CompactBuffer(1))), (shuke,(CompactBuffer(),CompactBuffer(2))), (kitty,(CompactBuffer(2),CompactBuffer())), (jerry,(CompactBuffer(3),CompactBuffer(2))))
    //把数据切分亚平,括号里面写的是正在切分亚平的方法
    val rdd4 = rdd3.flatMap({
          //把k放着不动,v提取出来left左是"(1,2),right右是(1),每一个都匹配1和2配,2和1配,yied返回匹配有的各种组合(迭代器)
      case (key,(left,right))=>{
        //当然是左右都有的才会匹配如果有一边没有就会过滤
        val newIter: Iterable[(Int, Int)] = for (v1 <- left; v2 <- right) yield (v1 , v2)
        //最后返回k和这个迭代器切分亚平
        newIter.map((key , _))
      }
    })

    //这是简写
//    val rdd4 = rdd3.flatMapValues(t => {
//      for (v1 <- t._1.iterator; v2 <- t._2) yield (v1, v2)
//    })
  }
}

FullOuterJoin的用法和源码分析

每一个元素都关联上哪一边没有的用None代替

package cn._51doit.demo01.Word

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

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

    val rdd1 = sc.parallelize(List(("tom", 1), ("tom", 2), ("jerry", 3), ("kitty", 2), ("kitty", 3)))
    val rdd2 = sc.parallelize(List(("jerry", 2), ("tom", 1), ("shuke", 2)))
    //直接演示fulloOuterJoin结果是ArrayBuffer((tom,(Some(1),Some(1))), (tom,(Some(2),Some(1))), (shuke,(None,Some(2))), (kitty,(Some(2),None)), (kitty,(Some(3),None)), (jerry,(Some(3),Some(2))))
    //每一个都连接再了一起,没有就用None代替
    val rdd3: RDD[(String, (Option[Int], Option[Int]))] = rdd1 fullOuterJoin (rdd2)
    println(rdd3.collect().toBuffer)

    //这个是通过cogroupby写
//    val rdd3: RDD[(String, (Iterable[Int], Iterable[Int]))] = rdd1.cogroup(rdd2)
//
//    val rdd4: RDD[(String, (Option[Int], Option[Int]))] = rdd3.flatMapValues(pair => {
//      if (pair._1.isEmpty) {
//        pair._2.iterator.map(e => (None, Some(e)))
//      } else if (pair._2.isEmpty) {
//        pair._1.iterator.map(e => (Some(e), None))
//      } else {
//        for (v1 <- pair._1.iterator; v2 <- pair._2.iterator) yield (Some(v1), Some(v2))
//      }
//    })
  }
}

lefrOuterJoin的用法和源码分析

就是左边一定有,如果左边有的右边没有就用None代替 ,如果左边没有右边有的就过滤掉

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

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

    val rdd1 = sc.parallelize(List(("tom", 1), ("tom", 2), ("jerry", 3), ("kitty", 2), ("kitty", 3)))
    val rdd2 = sc.parallelize(List(("jerry", 2), ("tom", 1), ("shuke", 2)))
//直接用leftOuterJoin方法演示得到的结果是ArrayBuffer((tom,(1,Some(1))), (tom,(2,Some(1))), (kitty,(2,None)), (kitty,(3,None)), (jerry,(3,Some(2))))
    val rdd3: RDD[(String, (Int, Option[Int]))] = rdd1.leftOuterJoin(rdd2)
    println(rdd3.collect().toBuffer)

    //下面是用cogroupup方法写的
//    val rdd3: RDD[(String, (Iterable[Int], Iterable[Int]))] = rdd1.cogroup(rdd2)
//
//    val rdd4 = rdd3.flatMapValues(pair => {
//      if(pair._2.isEmpty) {
//        pair._1.map((_, None))
//      } else {
//        for(v1 <- pair._1.iterator; v2 <- pair._2.iterator) yield (v1, Some(v2))
//      }
//    })
  }
}

rightOuterJoin的用法和源码分析

就是右边一定有,如果右边有的左边没有的就用None代替 ,如果是右边没有的左边有就过滤掉

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

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

    val rdd1 = sc.parallelize(List(("tom", 1), ("tom", 2), ("jerry", 3), ("kitty", 2), ("kitty", 3)))
    val rdd2 = sc.parallelize(List(("jerry", 2), ("tom", 1), ("shuke", 2)))
    //直接用rightOuterJoin方法演示结果是ArrayBuffer((tom,(Some(1),1)), (tom,(Some(2),1)), (shuke,(None,2)), (jerry,(Some(3),2)))
    val rdd3: RDD[(String, (Option[Int], Int))] = rdd1.rightOuterJoin(rdd2)
    println(rdd3.collect().toBuffer)

    //下面是用cogroupBy方法写的
//    val rdd3: RDD[(String, (Iterable[Int], Iterable[Int]))] = rdd1.cogroup(rdd2)
//    val rdd4: RDD[(String, (Option[Int], Int))] = rdd3.flatMapValues(pair => {
//      if(pair._1.isEmpty) {
//        pair._2.iterator.map((None, _))
//      } else {
//        for (v1 <- pair._1.iterator; v2 <- pair._2.iterator) yield (Some(v1), v2)
//      }
//    })
  }
}

intersection的使用和源码分析(交集)

就是求两个集合的交集并且去重了

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

object IntersectionDemo {
  def main(args: Array[String]): Unit = {
    //创建SparkContext,且是设置本地模式.setMaster("local[*]")
    val conf = new SparkConf().setAppName(this.getClass.getSimpleName).setMaster("local[*]")
    val sc = new SparkContext(conf)

    val rdd1 = sc.parallelize(List(1, 2, 3, 4, 5, 5, 6, 6), 2)
    val rdd2 = sc.parallelize(List(5,5,6,6,7,8,9,9,10),3)
    //直接用intersrction演示求交集,ArrayBuffer(6, 5)  结果会去重
//    val rdd3: RDD[Int] = rdd1.intersection(rdd2)
//    println(rdd3.collect().toBuffer)

    //下面是cogroup方法写交集     因为这个方法需要类型相同且是对偶元组所以先转化一下
    val rdd3: RDD[(Int, Null)] = rdd1.map((_, null))
    val rdd4: RDD[(Int, Null)] = rdd2.map((_, null))
    val rdd5: RDD[(Int, (Iterable[Null], Iterable[Null]))] = rdd3.cogroup(rdd4)
    val result = rdd5.filter(t => {
      t._2._1.nonEmpty && t._2._2.nonEmpty
    }).keys
    println(result.collect().toBuffer)
  }
}

subtract的使用和源码分析(差集)

就是把rdd1里面和rdd2有的过滤调,不去重
以后task里面的运算逻辑是一个迭代器链,迭代器链里面的一个运算逻辑对应RDD里面的一个conputer方法

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

object SutractDemo {
  def main(args: Array[String]): Unit = {
    //设置为本地模式.setMaster("local[*]")
    val conf = new SparkConf().setAppName(this.getClass.getSimpleName).setMaster("local[*]")
    val sc = new SparkContext(conf)

    val rdd1 = sc.parallelize(List(1,2,3,4,5,5,6,6), 2)
    val rdd2 = sc.parallelize(List(4,5,7), 2)
//直接用subtract实验得到的结果是ArrayBuffer(2, 6, 6, 1, 3)
    val rdd3: RDD[Int] = rdd1.subtract(rdd2)
    println(rdd3.collect().toBuffer)
  }
}

repatition和coalesce的区别

rdd1.repatition(3) 用一个rdd点repartition括号里面指定分区,就是说如果感觉原来的rdd分区太少了可以多指定一点分区让他的并行度变高当然变低也可以,或者原来的数据分区可能会有数据倾斜数据分配的不均匀就可以调用repatition打散重新分区,其实底层调用的是coalesce方法
一个stage里面有多少个task取决于这个stage最后一个RDD有多少个分区
rdd1.coalesce(3,false) 括号里填新的分区数和false或者true,true指定shuffle,false指定不shuffle,如果分区数量变少还false不shuffle的就相当于分区的合并,如果分区数量变多还false不suffle的那就做不到,还是原来的RDD
什么是suffle:在分布式计算中,将数据按照一定的计算逻辑(分区器),将具有相同规律的数据通过网络传输到指定的位置,严格的说是下游的task到上游拉取数据,上游的一个分区分到了下游的多个分区,就是shuffle(数据被打散了)
repartition:底层调用的是coalesce方法,shuffle=true,一定shuffle,指定分区数量
coalesce:可以shuffle,也可以不shuffle,增大分区数量 一定shuffle,减少分区数量不一定shuffle

sortByKey的用法

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

object SortByKeyDemo {
  def main(args: Array[String]): Unit = {
    //设置为本地模式.setMaster("local[*]")
    val conf = new SparkConf().setAppName(this.getClass.getSimpleName).setMaster("local[*]")
    val sc = new SparkContext(conf)

    val rdd1 = sc.parallelize(List(1,2,3,4,5,5,6,6,2.9,9,6,4,8), 2)
    val rdd2 = sc.parallelize(List(4,5,7), 2)
//就是在数据是对偶元组的时候可以对k进行排序
    val rdd3 = rdd1.map(i => (i,null))
    val rdd4 = rdd3.sortByKey()
    println(rdd4.collect().toBuffer)
  }
}

action算子的特点

在spark中action会生成job,collect会将计算好的结果搜集到drive端,如果触发action所有的算子一定会调用sc.runJob,把最后一个RDD传进去 ,也就是谁action算子一定会调用sc.runJob

aggregate的使用和源码分析

这边先定义一个集合在用aggregate聚合实验
val rdd = sc.parallelize(List("a","b","c","d"),2)
rdd.aggregate("$")(_+_ , _+_)          这个第一个参数是局部聚合的函数,第二个函数是全局聚合的函数
结果是 $$ab$cd    或者是     $$cd$ab
这说明了初始值局部聚合每个分区使用了一次,全局聚合也使用了一次所以游3个$
然后局部聚合是在Excuter端聚合的,全局聚合是在drive端聚合的

foreach算子的使用和讲解

foreach就是遍历数组
val rdd = sc.parallelize(List("a","b","c","d"),2)
rdd.foreach(e => println(e))
这个时候打印是在Excuter端因为这个时候两个分区还在Excuter端
rdd.collect.foreach(e => println(e))
这时候打印是在Drive端打印因为已经触发action算子返回到Drive端放在新的数组里调用数组的foreach

foreachPartition的用法

import java.sql.DriverManager
import org.apache.spark.{SparkConf, SparkContext}
/**
 * Aggregate是一个Action算子
 */
object ForeachPartitionDemo {

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

    //设置为本地模式.setMaster("local[*]")
    val conf = new SparkConf().setAppName(this.getClass.getSimpleName).setMaster("local[*]")
    val sc = new SparkContext(conf)

    val rdd1 = sc.parallelize(List(1,2,3,4,5, 6,7,8,9,10), 2)

    rdd1.collect()
	//这个是异步写的方法foreachPartitionAsync就是调用他之后程序运行可以往多个地方写,但是不能放在最后
    rdd1.foreachPartitionAsync(it => {
      it.foreach(e => {
      })
    })

	
    //foreach是一个Action
    rdd1.foreachPartition(it => {
      //事先创建好连接对象
      val connection = DriverManager.getConnection("", "", "")
      //遍历迭代器中的数据
      it.foreach(e => {
        //复用外面定义的connection,一个分区中的多条数据会使用一个连接对象,可以节省资源
      })

	  
//      while (it.hasNext) {
//        val e = it.next()
//
//      }

    })

    sc.stop()
  }
}

写入数据库的正确方式foreachPartition

在这里插入图片描述

Action行动算子

saveAsTextFile

count算子

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

/**
 * Count是一个Action算子
 * Count会在每一个分区内进行局部计数,来一条+1
 * 在Driver端进行全局的计算
 */
object CountDemo {

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

    //设置为本地模式.setMaster("local[*]")
    val conf = new SparkConf().setAppName(this.getClass.getSimpleName).setMaster("local[*]")
    val sc = new SparkContext(conf)

    val rdd1 = sc.parallelize(List(1,2,3,4,5, 6,7,8,9,10), 2)

    val r: Long = rdd1.count()
    println(r)
  }
}
最后打印10,就是求里面游多少条数据

sum算子fold算子reduce算子

他们都是先局部聚合在全局聚合

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

/**
 * Sum是一个Action算子
 * sum会在每一个分区内进行局部求和
 * 在Driver端进行全局的求和
 */
object SumDemo {

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

    //设置为本地模式.setMaster("local[*]")
    val conf = new SparkConf().setAppName(this.getClass.getSimpleName).setMaster("local[*]")
    val sc = new SparkContext(conf)

    val rdd1 = sc.parallelize(List(1,2,3,4,5, 6,7,8,9,10), 2)

    val r: Double = rdd1.sum()
    val i = rdd1.fold(100)(_ + _)
    val i1 = rdd1.reduce(_ + _)

    println(r)

  }
}

max和min算子

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

/**
 * min和max是一个Action算子
 * 会在每一个分区内进行局部两两比较大小
 * 在Driver端进行全局的比大小
 */
object MaxMinDemo {

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

    //设置为本地模式.setMaster("local[*]")
    val conf = new SparkConf().setAppName(this.getClass.getSimpleName).setMaster("local[*]")
    val sc = new SparkContext(conf)

    val rdd1 = sc.parallelize(List(1,2,3,4,5, 6,7,8,9,10), 2)

    val i = rdd1.min()
    val i1 = rdd1.max()
    println(i)
  }
}

take算子first算子

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

/**
 * Take是一个Action算子
 * 可能触发多次Action,
 * 每一次触发Action取一个分区的数据,不够n个在到下一个分区取
 */
object TakeDemo {

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

    //设置为本地模式.setMaster("local[*]")
    val conf = new SparkConf().setAppName(this.getClass.getSimpleName).setMaster("local[*]")
    val sc = new SparkContext(conf)

    val rdd1 = sc.parallelize(List(1,2,3,4,5, 6,7,8,9,10), 2)
    //从前面开始取元素,括号里写取几个
    val s: Array[Int] = rdd1.take(4)
    //返回第一个元素
    val i =rdd1.first()
    println(s.toBuffer)
  }
}

top和takeOrdered算子

top(n)取指定最大的n个
takeOrdered(n)取指定最小的n个

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

/**
 * Top是一个Action算子
 * 在每一个分区内使用有界优先队列对N个数据进行排序,移除不符合的数据
 * 在Driver端在将每个分区返回的结果用界优先队列进行全局的比大小
 * 有界优先队列类似一个TreeSet,但是不去重
 */
object TopDemo {

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

    //设置为本地模式.setMaster("local[*]")
    val conf = new SparkConf().setAppName(this.getClass.getSimpleName).setMaster("local[*]")
    val sc = new SparkContext(conf)

    val lst = List(4,9,7,5,2,  8,1,3,6,10)
    val rdd1 = sc.parallelize(lst, 2)
    //取指定最大的n个
    val ints = rdd1.top(2)
    //取指定最小的n个
    val ints1 = rdd1.takeOrdered(2)
    println(ints1.toBuffer)
  }
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值