spark三 算子简介

RDD中的所有转换都是延迟加载的,也就是说,它们并不会直接计算结果。相反的,它们只是记住这些应用到基础数据集(例如一个文件)上的转换动作。只有当发生一个要求返回结果给Driver的动作时,这些转换才会真正运行。这种设计让Spark更加有效率地运行。
也就是说Transformation算子,spark程序没有计算,遇到action算子开始计算

Transformation

单value结构
map(func)返回一个新的RDD,该RDD由每一个输入元素经过func函数转换后组成rdd.map((_,1))
filter(func)返回一个新的RDD,该RDD由经过func函数计算后返回值为true的输入元素组成x.filter(x % 2 == 0)
flatMap(func)类似于map,但是每一个输入元素可以被映射为0或多个输出元素rdd.flatMap(_.split(" "))
mapPartitions(func)类似于map,但独立地在RDD的每一个分片上运行,因此在类型为T的RDD上运行时,func的函数类型必须是Iterator[T] => Iterator[U]rdd.mapPartitions(x => x.map((_,1)))
mapPartitionsWithIndex(func)类似于mapPartitions,但func带有一个整数参数表示分片的索引值,因此在类型为T的RDD上运行时,func的函数类型必须是(Int, Iterator[T]) => Iterator[U]rdd.mapPartitionsWithIndex((index,items)=>(items.map((index,_))))
sample(withReplacement, fraction, seed)根据fraction指定的比例对数据进行采样,可以选择是否使用随机数进行替换,seed用于指定随机数生成器种子
glom将每一个分区形成一个数组,形成新的RDD[Array[T]]val value: RDD[Array[Int]] = rdd.glom()
groupBy按照传入函数的返回值进行分组。将相同的key对应的值放入一个迭代器val groupBy: RDD[(Int, Iterable[Int])] = rdd.groupBy(_%3)
distinct([numTasks]))对源RDD进行去重后返回一个新的RDDrdd.distinct()
coalesce(numPartitions)重新分区rdd.coalesce(3)
repartition(numPartitions)重新分区rdd.repartition(2)
sortByrdd.sortBy(x => x%3)
pipe管道,针对每个分区,都执行一个shell脚本,返回输出的RDD,注意:脚本需要放在Worker节点可以访问到的位置rdd.pipe("/opt/module/spark/pipe.sh")
双value结构
union(otherDataset)两个rdd的并集rdd1.union(rdd2)
intersection(otherDataset)两个rdd的交集rdd1.intersection(rdd2)
subtract两个rdd的差集rdd.subtract(rdd1)
zip将两个RDD组合成Key/Value形式的RDD,这但是两个RDD的partition数量以及元素数量都相同,否则会抛出异常
cartesian笛卡尔积,尽量避免使用rdd1.cartesian(rdd2).
val rdd1 = sc.parallelize(Array(1,2,3),3)
val rdd2 = sc.parallelize(Array("a","b","c"),3)
rdd1.zip(rdd2).collect

res1: Array[(Int, String)] = Array((1,a), (2,b), (3,c))
//foreachPartition一般对分区的一个元素和items操作,一般用来数据库操作
    key2CountActionRDD.foreachPartition{
      items =>
        val statArray = new ArrayBuffer[AdStat]()
        for (item <- items) {
          val keySplited: Array[String] = item._1.split("_")
          val date = keySplited(0)
          val province = keySplited(1)
          val city = keySplited(2)
          val adid = keySplited(3).toLong

          val clickCount = item._2
          statArray += AdStat(date, province, city, adid, clickCount)
        }
        AdStatDAO.updateBatch(statArray.toArray)
    }
k-v结构
partitionBy按照k进行分区rdd.partitionBy(new org.apache.spark.HashPartitioner(2))
groupByKey([numTasks])在一个(K,V)的RDD上调用,返回一个(K, Iterator[V])的RDDval groupByKey: RDD[(Int, Iterable[Int])] = kvRdd.groupByKey()
sortByKey([ascending], [numTasks])只能按照k排序,K必须实现Ordered接口rdd.sortByKey(true)
mapValues针对于(K,V)形式的类型只对V进行操作rdd3.mapValues("@"+_) //对每个value前面都加上字符串@
join在类型为(K,V)和(K,W)的RDD上调用,返回一个相同key对应的所有元素对在一起的(K,(V,W))的RDD
cogroup在类型为(K,V)和(K,W)的RDD上调用,返回一个(K,(Iterable,Iterable))类型的RDD
reduceByKey(func, [numTasks])在一个(K,V)的RDD上调用,返回一个(K,V)的RDD,使用指定的reduce函数,将相同key的值聚合到一起,与groupByKey类似,reduce任务的个数可以通过第二个可选的参数来设置val reduceByKey = kvrdd.reduceByKey(+)
aggregateByKey(zeroValue)(seqOp, combOp, [numTasks])
foldByKey
combineByKey
join
> val rdd = sc.parallelize(Array((1,"a"),(2,"b"),(3,"c")))
> val rdd1 = sc.parallelize(Array((1,4),(2,5),(3,6)))
> rdd.join(rdd1).collect()
res13: Array[(Int, (String, Int))] = Array((1,(a,4)), (2,(b,5)), (3,(c,6)))
cogroup
val rdd = sc.parallelize(Array((1,"a"),(2,"b"),(3,"c")))
val rdd1 = sc.parallelize(Array((1,4),(2,5),(3,6)))
rdd.cogroup(rdd1).collect()
res14: Array[(Int, (Iterable[String], Iterable[Int]))] = Array((1,(CompactBuffer(a),CompactBuffer(4))), (2,(CompactBuffer(b),CompactBuffer(5))), (3,(CompactBuffer(c),CompactBuffer(6))))
aggregateByKey

参数:(zeroValue:U,[partitioner: Partitioner]) (seqOp: (U, V) => U,combOp: (U, U) => U)

  1. 作用:在kv对的RDD中,,按key将value进行分组合并,合并时,将每个value和初始值作为seq函数的参数,进行计算,返回的结果作为一个新的kv对,然后再将结果按照key进行合并,最后将每个分组的value传递给combine函数进行计算(先将前两个value进行计算,将返回结果和下一个value传给combine函数,以此类推),将key与计算结果作为一个新的kv对输出。
  2. 参数描述:
    (1)zeroValue:给每一个分区中的每一个key一个初始值;
    (2)seqOp:函数用于在每一个分区中用初始值逐步迭代value;
    (3)combOp:函数用于合并每个分区中的结果。
  3. 经典需求:创建一个pairRDD,取出每个分区相同key对应值的最大值,然后相加
    在这里插入图片描述
> val rdd = sc.parallelize(List(("a",3),("a",2),("c",4),("b",3),("c",6),("c",8)),2)
> val agg = rdd.aggregateByKey(0)(math.max(_,_),_+_)
> agg.collect()
res0: Array[(String, Int)] = Array((b,3), (a,3), (c,12))
foldByKey

参数:(zeroValue: V)(func: (V, V) => V): RDD[(K, V)]

  1. 作用:aggregateByKey的简化操作,seqop和combop相同
  2. 需求:创建一个pairRDD,计算相同key对应值的相加结果
> val rdd = sc.parallelize(List((1,3),(1,2),(1,4),(2,3),(3,6),(3,8)),3)
> val agg = rdd.foldByKey(0)(_+_)
> agg.collect()
res61: Array[(Int, Int)] = Array((3,14), (1,9), (2,3))
combineByKey

参数:(createCombiner: V => C, mergeValue: (C, V) => C, mergeCombiners: (C, C) => C)

  1. 作用:对相同K,把V合并成一个集合。
  2. 参数描述:
    (1)createCombiner: combineByKey() 会遍历分区中的所有元素,因此每个元素的键要么还没有遇到过,要么就和之前的某个元素的键相同。如果这是一个新的元素,combineByKey()会使用一个叫作createCombiner()的函数来创建那个键对应的累加器的初始值
    (2)mergeValue: 如果这是一个在处理当前分区之前已经遇到的键,它会使用mergeValue()方法将该键的累加器对应的当前值与这个新的值进行合并
    (3)mergeCombiners: 由于每个分区都是独立处理的, 因此对于同一个键可以有多个累加器。如果有两个或者更多的分区都有对应同一个键的累加器, 就需要使用用户提供的 mergeCombiners() 方法将各个分区的结果进行合并。
  3. 经典需求:创建一个pairRDD,根据key计算每种key的均值。(先计算每个key出现的次数以及可以对应值的总和,再相除得到结果)
    在这里插入图片描述
val input = sc.sparkContext.parallelize(Array(("a", 88), ("b", 95), ("a", 91), ("b", 93), ("a", 95), ("b", 98)), 2)
val combine: RDD[(String, (Int, Int))] = input.combineByKey(
  (_, 1),
  (acc: (Int, Int), v) => (acc._1 + v, acc._2 + 1),
  (acc1: (Int, Int), acc2: (Int, Int)) => (acc1._1 + acc2._1, acc1._2 + acc2._2))
println(combine.collect().mkString(""))//(b,(286,3))(a,(274,3))

val res: RDD[(String, Double)] = combine.map {
  case (key, value) =>
    (key, value._1 / value._2.toDouble)
}
println(res.collect().mkString(""))//(b,95.33333333333333)(a,91.33333333333333)

Action

算子介绍
reduce(func)通过func函数聚集RDD中的所有元素,这个功能必须是可交换且可并联的
collect()在驱动程序中,以数组的形式返回数据集的所有元素
count()返回RDD的元素个数rdd.count
first()返回RDD的第一个元素(类似于take(1))
take(n)返回一个由数据集的前n个元素组成的数组
takeSample(withReplacement,num, [seed])返回一个数组,该数组由从数据集中随机采样的num个元素组成,可以选择是否用随机数替换不足的部分,seed用于指定随机数生成器种子
takeOrdered(n,?[ordering])takeOrdered和top类似,只不过以和top相反的顺序返回元素
saveAsTextFile(path)将数据集的元素以textfile的形式保存到HDFS文件系统或者其他支持的文件系统,对于每个元素,Spark将会调用toString方法,将它装换为文件中的文本
saveAsSequenceFile(path)将数据集中的元素以Hadoop sequencefile的格式保存到指定的目录下,可以使HDFS或者其他Hadoop支持的文件系统。
saveAsObjectFile(path)
countByKey()针对(K,V)类型的RDD,返回一个(K,Int)的map,表示每一个key对应的元素个数。rdd.countByKey
foreach(func)在数据集的每一个元素上,运行函数func进行更新。

map与flatMap的区别以及mapPartiton和flatMapPartition 以及foreach和foreachPartition

map是一对一操作,输入一个元素,就输出一个元素,返回新的RDD
flatMap是输入一个元素,但是输出可以使0个或者多个元素,返回新的RDD
foreach也会遍历每一个元素,但是他是没有返回值的,不会生成RDD,而且它是一个action算子

他们都是应用于每一个元素也就是每条数据执行一次,task为每个数据,都要去执行一次function函数。

而partition算子一个task仅仅会执行一次function,function一次接收所有的partition数据。只要执行一次就可以了,性能比较高,但如果数据量过大的话会导致一个分区的数据放到内存中执行导致oom
适用于分析的数据量不是特别大的时候,或者估算一下RDD的数据量,以及每个partition的量,还有自己分配给每个executor的内存资源。看看一下子内存容纳所有的partition数据行不行

reduce与groupby的区别

reduceByKey在shuffle之前进行分区内分组聚合,在到reduce进行分区间分组聚合,减少reduce的压力
groupByKey所有的kv都会移动
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

orange大数据技术探索者

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值