RDD常用算子

RDD转换算子

功能的补充和封装,将旧的RDD包装成新的RDD
RDD根据数据处理方式的不同将算子整体上分为value类型、双value类型和Key-Value类型

value类型

map

将处理的数据逐条进行映射转换,这里的转换可以是类型的转换,也可以是值的转换

val rdd = sc.parallelize(Array(1,2,3,4))
val mapRDD = rdd.map(_*2)
mapRDD.collect.foreach(println)
//2
//4
//6
//8

mapPartitions

可以以分区为单位进行数据转换操作,但是会将整个分区的数据加载到内存中进行引用,如果处理完的数据时不会被释放的,存在对象的引用。

val rdd = sc.makeRDD(Array(1,2,3,4),2)
val mp = rdd.mapPartitions(
  iter => {
    println("------")
    iter.map(_*2)
  }
)
mp.collect.foreach(println)
//------
//------
//2
//4
//6
//8

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

mapPartitionsWithIndex

将待处理的数据以分区为单位发送到计算节点进行处理,这里的处理是指可以进行任意的处理

val rdd = sc.makeRDD(Array(1,2,3,4),2)
val mp = rdd.mapPartitionsWithIndex(
  (index,iter) => {
    if(index ==1){
        iter
    }else{
       Nil.iterator
    }
  }
)
mp.collect.foreach(println)
//3
//4

flatMap

将处理的数据进行扁平化后再进行映射处理

val rdd = sc.makeRDD(Array(Array(1,2),Array(3,4)))
rdd.flatMap(x=>x).collect.foreach(println)
//1
//2
//3
//4

glom

将同一个分区的数据直接转换为相同类型的内存数组进行处理,分区不变

val rdd = sc.makeRDD(Array(1,2,3,4),2)
val value = rdd.glom()
value.collect.foreach(x=>println(x.mkString(",")))
1,2
3,4

分区内取最大值,两个分区之间最大值相加

val rdd = sc.makeRDD(Array(1,2,3,4),2)
val value = rdd.glom()
val maxValue = value.map(array=>array.max)
println(maxValue.collect.sum)
//6

groupBy

将数据根据指定的规则进行分组,分区默认不变,但是数据会被打乱重新组合,我们将这样的操作称之为shuffle。

val rdd = sc.makeRDD(Array(1,2,3,4),2)
val group = rdd.groupBy(x=>x%2)
group.collect.foreach(println)
(0,CompactBuffer(2, 4))
(1,CompactBuffer(1, 3))

根据首写字母进行分组

 val rdd = sc.makeRDD(Array("Hello","Spark","Scala","Hadoop"),2)
val group = rdd.groupBy(_.charAt(0))
group.collect.foreach(println)
(H,CompactBuffer(Hello, Hadoop))
(S,CompactBuffer(Spark, Scala))

分组和分区没有必然的关系

filter

将数据根据指定的规则进行筛选过滤,符合规则的数据保留,不符合规则的数据丢弃,当数据进行筛选过滤后,分区不变,但是分区内的数据可能不均衡,生产环境下,可能会出现数据倾斜

 val rdd = sc.makeRDD(Array(1,2,3,4))
 val fi = rdd.filter(x=>x%2==0)
 fi.collect.foreach(println)
 //2
 //4

sample

根据指定的规则从数据集中抽取数据

      val rdd = sc.makeRDD(Array(1,2,3,4,5,6,7,8,9,10))
      //第一个参数,抽取数据后是否将数据放回
      //第二个参数,数据源中每条数据被抽取的概率
      //基准值的概念
      //第三个参数,抽取数据随机算法的种子
      //如果不传递第三个参数,使用的是当前系统时间
      println(rdd.sample(false, 0.4)
      .collect.mkString(","))
      //2,3,4,5,9

distinct

去重

    val rdd = sc.makeRDD(Array(1,2,3,4,1,2,3,4))
    val di = rdd.distinct()
    di.collect.foreach(println)
    //1 2 3 4

coalesce

根据数据量缩减分区,用于大数据集过滤后,提高小数据集的执行效率

    val rdd = sc.makeRDD(Array(1,2,3,4,5,6),3)
    //coalesce默认情况下不会将分区的数据打乱重新组合
    //这种情况下的缩减分区会导致数据不均衡,出现数据均衡
    //如果想要让数据均衡,可以进行shuffle处理
    //val newRDD = rdd.coalesce(2)
    val newRDD = rdd.coalesce(2,true)
    newRDD.saveAsTextFile("out")
    //1 3 5
    //2 4 6 shuffle的效果

repartition

扩大分区

    val rdd = sc.makeRDD(Array(1,2,3,4,5,6),2)
    //coalesce是可以扩大分区的,但是如果不进行shuffle操作是没有意义的
    //spark提供了简化操作
    //缩减分区:coalesce 想要数据均衡,可以采用shuffle
    //扩大分区:repartition
    //val newRDD = rdd.coalesce(3,true)
    val newRDD = rdd.repartition(3)
    newRDD.saveAsTextFile("out")

sortBy

 val rdd = sc.makeRDD(Array(3,5,2,6,1,4),2)
 val sortRDD = rdd.sortBy(x=>x)  //升序
 sortRDD.collect.foreach(println)
val rdd = sc.makeRDD(Array(("1",2),("11",3),("2",4)),2)
val sortRDD = rdd.sortBy(x=>x._1)
sortRDD.collect.foreach(println)
(1,2)
(11,3)
(2,4)

双value类型

intersection union subtract zip

   val rdd1 = sc.makeRDD(Array(1,2,3,4))
    val rdd2 = sc.makeRDD(Array(3,4,5,6))
    //交集
    val rdd3 = rdd1.intersection(rdd2)
    println(rdd3.collect.mkString(","))
    //并集
    val rdd4 = rdd1.union(rdd2)
    println(rdd4.collect.mkString(","))
    //差集
    val rdd5 = rdd1.subtract(rdd2)
    println(rdd5.collect.mkString(","))
    //拉链
    val rdd6 = rdd1.zip(rdd2)
    println(rdd6.collect.mkString(","))
3,4
1,2,3,4,3,4,5,6
1,2
(1,3),(2,4),(3,5),(4,6)

key-value类型

partitionBy

将数据按照指定partitioner重新进行分区,spark默认的分区器是HashPartitioner

 val rdd = sc.makeRDD(Array(1,2,3,4))
    val mapRDD = rdd.map((_,1))
    //根据规定的分区规则对数据进行重分区
    mapRDD.partitionBy(new HashPartitioner(2)).saveAsTextFile("out")
(2,1)
(4,1)在一个分区
(1,1)
(3,1)

reduceByKey

reduceByKey分区内和分区间计算规则是相同的

    val rdd = sc.makeRDD(Array(("a",1),("a",2),("a",3),("b",4)))
    //reduceByKey:相同的key进行value数据的聚合操作
    //如果key的数据只有一个时,是不会参与计算的
    rdd.reduceByKey(_+_).collect.foreach(println)
    (a,6)
    (b,4)

groupByKey

val rdd = sc.makeRDD(Array(("a",1),("a",2),("a",3),("b",4)))
    //groupByKey 将数据源中的数据,相同的key的数据分在一个组中,形成一个对偶元组
    //元组中的第一个元素是key
    //元组中的第二个元素就是相同key的value的集合
    val group = rdd.groupByKey()
    group.collect.foreach(println)
   //(a,CompactBuffer(1, 2, 3))
   //(b,CompactBuffer(4))

aggregateByKey

将数据根据不同的规则进行分区内计算和分区间计算

    val rdd = sc.makeRDD(Array(("a",1),("a",2),("a",3),("a",4)),2)
     //aggregateByKey存在函数柯里化,有两个参数列表
    //第一个参数列表:初始值
    //第二个参数列表:
    //第一个参数表示分区内计算规则
    //第二个参数表示分区间计算规则
    rdd.aggregateByKey(0)(
      (x,y)=> math.max(x,y),
      (x,y)=> x + y
    ).collect.foreach(println)
    (a,6)

foldByKey

val rdd = sc.makeRDD(Array(("a",1),("a",2),("b",3),("b",4),
  ("b",5),("a",3)),2)
    //如果聚合计算时,分区内和分区间计算规则相同,提供了简化的方法
    rdd.foldByKey(0)(_+_).collect.foreach(println)
(b,12)
(a,6)

RDD行动算子

触发任务的调度和作业的执行

aggregate

分区的数据通过初始值和分区内的数据进行聚合,然后再和初始值进行分区间的数据聚合

val rdd = sc.makeRDD(Array(1,2,3,4),2)
    println(rdd.aggregate(5)(_ + _, _ + _))
    rdd.aggregate(5)(
      (x,y)=>{println("x":+x+"y":+y);x+y},
      (x,y)=>{println("x":+x+"y":+y);x+y}
    )

在这里插入图片描述

fold

分区内和分区间计算规则是相同的
是aggregate的简化
有初始值,只有一个函数

val rdd = sc.makeRDD(Array(1,2,3,4),2)
println(rdd.fold(5)(_ + _))
//25

countByValue

val rdd = sc.makeRDD(Array(1,2,3,4))
    val value = rdd.countByValue()
    println(value)
    Map(4 -> 1, 2 -> 1, 1 -> 1, 3 -> 1)

countByKey

    val rdd = sc.makeRDD(Array(("a",1),("a",4),("b",3)))
    val key = rdd.countByKey()
    println(key)
   //Map(a -> 2, b -> 1)

saveAsTextFile saveAsObjectFile saveAsSequenceFile

val rdd = sc.makeRDD(Array(("a",1),("a",4),("b",3)),2)
    rdd.saveAsTextFile("out")
    rdd.saveAsObjectFile("out1")
    //saveAsSequenceFile类型是k-v类型
    rdd.saveAsSequenceFile("out1")

foreach

遍历

    val rdd = sc.makeRDD(Array(1,2,3,4))
    //foreach Driver端内存集合的循环遍历方法
    rdd.collect.foreach(println)
    println("----------")
    //foreach executor端内存数据打印
    rdd.foreach(println)

在这里插入图片描述
算子:RDD的方法和scala集合对象的方法不一样
集合对象的方法都是在同一个节点的内存中完成的
RDD的方法可以将计算逻辑发送到Executor端(分布式节点)执行
为了区分不同的处理效果,所以将RDD的方法称之为算子
RDD的方法的操作都是在Driver端执行的,而方法内部的逻辑代码是在Executor端执行的

foreachAsync --异步处理

foreachPartition

分区遍历

//每个分区就是一个迭代器
//it.foreach 是scala的foreach函数
val rdd1 = sc.parallelize(Array(1,2,3,4),2)
    rdd1.foreachPartition(
      it=> it.foreach(x=> println(x))
    )  //3 1 2 4

foreach 是一条一条的拿数据进行处理
foreachPartition是一个分区一个分区的拿数据,一个分区中有很多数据的信息。
所以,在使用中,当我们要把处理结果保存到数据库中的时候,我们要使用foreachPartition这个方法,这样效率会更高。
2.通过上面的例子也说明了,foreach和foreachPartition这两个方法执行完之后不会将结果收集到driver端。
而collect,top,count这些方法是会把结果收集到driver端的。

foreachPartitionAsync-异步

++

类似union

val rdd1 = sc.parallelize(1 to 10)
val rdd2 = sc.parallelize(11 to 20)
val a = rdd1 ++ rdd2
println(a.collect.mkString(","))
//1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20

barrier

在这里插入代码片

Cache型

cache

    val conf = new SparkConf().setMaster("local[*]").setAppName("Cachedemo")
    val sc = SparkContext.getOrCreate(conf)
    val rdd1 = sc.textFile("in/users.csv")
    rdd1.cache()
    var start = System.currentTimeMillis()
    println(rdd1.count())//执行完动作算子才缓存
    var end = System.currentTimeMillis()
    println("第一次打印数量时间差:" + (end - start))
    start = System.currentTimeMillis()
    println(rdd1.count())
    end = System.currentTimeMillis()
    println("第二次打印数量时间差:" + (end - start))
    start = System.currentTimeMillis()
    println(rdd1.count())
    end = System.currentTimeMillis()
    println("第三次打印数量时间差:" + (end - start))

在这里插入图片描述

persist

    val conf = new SparkConf().setMaster("local[*]").setAppName("Cachedemo")
    val sc = SparkContext.getOrCreate(conf)
    val rdd1 = sc.textFile("in/users.csv")
    //rdd1.cache() //设置缓存的方法
    rdd1.persist(StorageLevel.MEMORY_ONLY_2)
    var start = System.currentTimeMillis()
    println(rdd1.count())//执行完动作算子才缓存
    var end = System.currentTimeMillis()
    println("第一次打印数量时间差:" + (end - start))
    start = System.currentTimeMillis()
    println(rdd1.count())
    end = System.currentTimeMillis()
    println("第二次打印数量时间差:" + (end - start))
    start = System.currentTimeMillis()
    println(rdd1.count())
    end = System.currentTimeMillis()
    println("第三次打印数量时间差:" + (end - start))
    start = System.currentTimeMillis()
    rdd1.unpersist() //让缓存失效
    println(rdd1.count())
    end = System.currentTimeMillis()
    println("第四次打印数(缓存失效)量时间差:" + (end - start))

canEqual

val rdd1 = sc.parallelize(Array(1,2,3,4))
rdd1.canEqual(1)  //false

cartesian

val rdd1 = sc.parallelize(Array(1,2,3,4))
val rdd2 = sc.parallelize(5 to 10)
rdd1.cartesian(rdd2).collect

在这里插入图片描述

checkpoint

val conf = new SparkConf().setMaster("local[*]").setAppName("Cachedemo")
    val sc = SparkContext.getOrCreate(conf)
    //设置检查点路径
    sc.setCheckpointDir("file:///D:/kb11checkpointdir")
    val rdd = sc.parallelize(Array(("a",1),("b",2),("c",3)))
    rdd.checkpoint()
    rdd.collect()
    println("是否checkpoint:"+rdd.isCheckpointed)
    println(rdd.getCheckpointFile)

在这里插入图片描述

collect

相当于toArray,collect将分布式的RDD返回为一个单机的scala Array数组,在这个数组上运用scala的函数式操作;

val rdd = sc.parallelize(Array(1,2,3,4))
rdd.collect.foreach(println)

collectAsync

异步

val rdd1 = sc.parallelize(Array(1,2,3,4),2)
println(rdd1.collectAsync().get())
WrappedArray(1, 2, 3, 4)

compute

context、sparkContext

返回创建rdd时的SparkContext

    val conf = new SparkConf().setMaster("local[*]").setAppName("Practice")
    val sc = SparkContext.getOrCreate(conf)
    println(sc)
    val rdd = sc.parallelize(Array(1,2,3,4))
    println(rdd.context)
    println(rdd.sparkContext)

在这里插入图片描述

copy

count

统计个数

val rdd1 = sc.parallelize(Array(1,2,3,4))
rdd1.count  //4

dependencies

查看当前rdd是否有依赖,可以帮助恢复重建rdd

    val rdd = sc.parallelize(Array(1,2,3,4))
    println(rdd)
    println(rdd.dependencies)
    val rdd1 = rdd.map(_*2)
    println(rdd1.dependencies)

在这里插入图片描述

first

第一个元素

val rdd1 = sc.parallelize(Array(1,2,3,4))
rdd1.first    //1

getNumPartitions partitions.size

获取分区数

   val rdd = sc.parallelize(Array(1,2,3,4),2)
    println(rdd.getNumPartitions) //2
    println(rdd.partitions.size)  //2

getStorageLevel

存储等级

    val rdd = sc.parallelize(Array(1,2,3,4),2)
    println(rdd.getStorageLevel)
    //StorageLevel(1 replicas)

histogram-直方图

输入的参数buckets可以是一个数字,也可以是一个列表
输出结果为一个元组,元组包含两个列表分别是桶(直方图的边界)和直方图的频数
【注意】:
1、桶必须是排好序的,并且不包含重复元素,至少有两个元素
2、所有直方图的结果集合区右边是开区间,最后一个区间除外。
计算方式 : 一、参数buckets是一个数字的情况: 根据桶的总数来计算
先用排好序的数组的边界值来得出两个桶之间的间距 (9.0-1.1)/5 = 1.58
所以得到第一个元组(直方图的边界) 1.1-2.68 2.68-4.26 以此类推
然后计算每个桶中的频数 : 数组中的数字在桶中的分布情况, 位于第一个区间(1.1~2.68) 中的数字有 1.1、1.2、1.3、2.0、2.1 一共5个 对应第二个数组中的第一个数字

val a = sc.parallelize(List(1.1,1.2,1.3,2.0,2.1,7.4,7.5,7.6,8.8,9.0),3)
a.histogram(5)
res0:(Array[Double], Array[Long]) = (Array(1.1, 2.68, 4.26, 5.84, 7.42, 9.0),Array(5, 0, 0, 1, 4))

id

返回rdd的编号

    val rdd1 = sc.parallelize(Array(1,2,3,4),2)
    println(rdd1.id)

isEmpty

val rdd1 = sc.parallelize(1 to 5)
rdd1.isEmpty   //false

keyBy

指定一个函数产生的数据作为rdd的key

    val rdd1 = sc.parallelize(Array("hello","scala","java","python"))
    rdd1.keyBy(_.length).collect.foreach(println)

在这里插入图片描述

keys

获取rdd中的元组的key,key可以重复出现,List类型可以使用

    val rdd1 = sc.parallelize(Array((1,"hello"),(2,"java"),(3,"scala"),(4,"spark")))
    println(rdd1.keys.collect.mkString(",")) //1,2,3,4

join

join 用于两个key-value类型的RDD的内连接操作,类似于数据库中的内连接。只有两者的key相同时,才会连接

val a = sc.parallelize(List("dog", "salmon", "salmon", "rat", "elephant"), 3)
val b = a.keyBy(_.length)
val c = sc.parallelize(List("dog","cat","gnu","salmon","rabbit","turkey","wolf","bear","bee"), 3)
//相同的key,就能连接在一起
val d = c.keyBy(_.length)
b.join(d).collect 
res0: Array[(Int, (String, String))] = Array((6,(salmon,salmon)), (6,(salmon,rabbit)), (6,(salmon,turkey)), (6,(salmon,salmon)), (6,(salmon,rabbit)), (6,(salmon,turkey)), (3,(dog,dog)), (3,(dog,cat)), (3,(dog,gnu)), (3,(dog,bee)), (3,(rat,dog)), (3,(rat,cat)), (3,(rat,gnu)), (3,(rat,bee)))


max min mean sum

val rdd1 = sc.parallelize(1 to 5)
rdd1.max  //5
rdd1.min  //1
rdd1.mean //3.0
rdd1.sum  //15.0

take

val rdd1 = sc.parallelize(1 to 5)
val a = rdd1.take(5)
println(a.mkString(","))  //1,2,3
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值