spark.3 rdd算子

目录

摘要

一:join 会进行shuffer

二:union 不会进行shuffer

三:intersectio 会进行shuffer

四:groupBy 会进行shuffer

五: groupByKey 会进行shuffer

六:redceByKey会进行shuffer

七:repatation()和partitionBy区别

八:aggregateByKey 会进行shuffer


摘要

关于spark一些概念的知识见另外一篇博文,这一篇章之说一些算子。

一:join 会进行shuffer

join 用于将<k,v>键值对的算子进行连接操作,最后生成两个rdd算子共同拥有key的数据结构。

方法的定义:def  join[W](other: RDD[ (K,V)]  ):RDD[ (K , (V1,V2) ) ]

val thisRDD=sc.makeRDD(  Array(("A","1"), ("A","2"),("B","2"),("B","4")("C","6") )      )

val otherRDD=sc.makeRDD(  Array(("A","2"), ("A","8"),("B","5")     )

thisRDD.join(otherRDD).collect.foreach(println)

(A,(1,2))

(A,(1,8))

(A,(3,2))

(A,(3,8))

(B,(2,5))

(B,(4,5))

根据上面的结果可以知道,join之后的结果会去掉不相交的key ,  (C,6)这条数据被无情抛弃。

二:union 不会进行shuffer

返回两个rdd的并集,如果集合中有重复的元素则不会进行去重,就是单纯的求并集。

def  union(other:RDD[T]):RDD[T]

val rdd1 =sc.makeRDD(Array(1,2,3,3))

val rdd2 =sc.makeRDD(Array(4,5,6,6))

rdd1.union(rdd2).collect.foreach(print)

输出结果为:1 2 3 3 4 5 6 6

三:intersectio 会进行shuffer

intersection操作和union相反,求的是两个rdd的交集且会进行去重。

方法定义: def  intersection(other:RDD[T]):RDD[T]

val rdd1 =sc.makeRDD(Array(1,2,3,3))

val rdd2 =sc.makeRDD(Array(1,2,4,5,6,6))

rdd1.union(rdd2).collect.foreach(print)

输出结果是:1  2

四:groupBy 会进行shuffer

group接受一个函数,rdd会根据函数的定义对rdd中的数据进行分组,并返回新的rdd,新的rdd是一个元组,

数据格式(组号,该组的迭代器)

函数定义:def groupBy[K] (f:(t)=>K) :RDD[(K,Iterable[T])]     k是f函数返回的值

注意理解这个定义,groupBy接收一个函数f,该函数经过运算之后返回一个类型为K 的值,值相同的元素会划分到同一组。划分完之后结果用RDD[ (K,Iterable[T]) ]返回。  这个结果中包含所有分组的数据,rdd[]  数组中每一条元素都是一个元组,该元组的样式就是:(K,Iterable[T])

val rdd=sc.makeRDD(Array(1,2,3,4,5,6,7,8,9,10)).groupBy(t=>t%2)

rdd.collect.foreach(x=>println("组号:"+x._1 +" 迭代器数据:"+x_2.mkString(" ")))

1%2=1                4%2=0

2%2=0                5%2=1

3%2=1                6%2=0

根据上面的计算结果可以知道,t=>t%2   这个匿名函数的返回值只有两种,0 和1 也就意味着返回为0的数据在一个分区,返回为1 的数据在一个分区。

所以输出结果为:

组号:0  迭代器数据:2 4 6

组号:1 迭代器数据: 1 2 3

五: groupByKey 会进行shuffer

groupByKey和join一样 操作的是<k,v>键值对,且这个方法是没有参数的。和groupBy有一定的区别但是也相似,二者都是分组,不过groupby是操作的是数组数据,groupbykey操作的是<k,v>键值对。二者的返回值类型都是RDD[ (k,Interable[T]) ],仔细品品,其实没意义讨论区别,理解了各自操作的数据返回的数据,你该用哪个用哪个就好了。

函数定义:def groupByKey():RDD[ (k,Interable[T]) ]

val rdd1=sc.makeRDD(Array(  ("A","1"),("A","3"),("B","2"),("B","4"),("B","6"),("c","8")   ));

val rdd2=rdd1.groupByKey;

rdd2.collect.foreach(x=>println("key:"+x._1 +" 迭代器数据:"+x_2.mkString(" "))))

key:A 迭代器数据:1 3

key:B 迭代器数据:2 4 6

key:C 迭代器数据:8

注意点

  • 过程中涉及到的<k,v>都是在内存中操作,容易内存溢出
  • 和groupBy一样效率底下请谨慎使用
  • 可以采取HashPartition进行优化,hash之后相当于对rdd重新分区,,这样就提前shuffer。相同key放在一个分区中,这样进行groupbykey的时候就不会涉及到宽依赖shuffer了。

六:redceByKey会进行shuffer

reducebykey 会对rdd<k,v>键值对  key相同的元素进行合并处理。

方法定义: def reduceByKey(fun:(v,v)=>v):RDD[(K,V)]

仔细看这个函数,我们来分析reduceByKey这个方法的参数(fun:(v,v)=>v)(fun:(v,v)=>v)是一个function函数,这个函数的参数是两个value(注意不是key,而且这两个value对应的key一定是相同的,而且是依次传入两个value),传入两个value之后返回的是一个value  , 然后再找出下一个相同key的value和上次返回的那个value再一次调用(fun:(v,v)=>v),依次往后,直到该key的所有的value处理完了之后获得最终的一个终极value,这个中级value 和key  组合成(k,v)的样式。当调触发action方法调用结束之后,所有的(k1,v1),(k2,v2)最为数组返回也即是最终的RDD[(K,V)]。 

val rdd1=sc.makeRDD(Array(  ("A","1"), ("A","2") ("A","3") ("B","2") ("B","2") ) )

val rdd2=rdd1.reduceByKey( (v1,v2)=>{

v1+v2

} );

rdd2.collect.foreach(x=>println(x))

结果如下:

(A,6)

  (B,4)

七:repatation()和partitionBy区别

   repatation源码如下:注意 var position = (new Random(index)).nextInt(numPartitions)  整体看下来其实repatation重新分区再分区之前加入了随机数,这样的话就意味着hash之后真正的随机分配,哪怕key相同依旧大概率分配到不同的分区。

def repartition(numPartitions: Int)(implicit ord: Ordering[T] = null): RDD[T] = withScope {
	coalesce(numPartitions, shuffle = true)
}


def coalesce(numPartitions: Int, shuffle: Boolean = false)(implicit ord: Ordering[T] = null)
      : RDD[T] = withScope {
    if (shuffle) {
      /** Distributes elements evenly across output partitions, starting from a random partition. */
      val distributePartition = (index: Int, items: Iterator[T]) => {
        var position = (new Random(index)).nextInt(numPartitions)
        items.map { t =>
          // Note that the hash code of the key will just be the key itself. The HashPartitioner
          // will mod it with the number of total partitions.
          position = position + 1
          (position, t)
        }
      } : Iterator[(Int, T)]

      // include a shuffle step so that our upstream tasks are still distributed
      new CoalescedRDD(
        new ShuffledRDD[Int, T, T](mapPartitionsWithIndex(distributePartition),
        new HashPartitioner(numPartitions)),
        numPartitions).values
    } else {
      new CoalescedRDD(this, numPartitions)
    }
}

   partitionBy则是作用于pairRDD,用key的hash进行分区。

   相同点,二者都是用HashPartitioner进行分区的。

八:aggregateByKey 会进行shuffer

和reduceByKey一样都是对<k,v>进行操作 ,不同点就一处reduceByKey(f:(v,v)=>v)   无法改变返回值的类型  你看吧  都是v

aggregateByKey力度比较细,其参数的写法是柯里化的样式,什么是柯里化请大家自己查询这里不作赘述。

函数定义如下,重点看红色部分。

def aggregateByKey[U: ClassTag](zeroValue: U, partitioner: Partitioner)(seqOp: (U, V) => U, combOp: (U, U) => U): RDD[(K, U)]

zeroValue: U  是传入的第一个参数

Partitioner 可以不用管先,默认会用hashPartation

seqOp: (U, V)这个函数作用在每一个分区,对各个分区key相同的元素依次执行逻辑操作。

combOp: (U, U) 这个函数是shuffer ,将seqOp: (U, V)处理好的数据,在不同分区key相同的元素拉过来执行逻辑操作。 最后将结果封装成RDD[(K, U)] 数组。

上面seqOp  combOp是两个自定义的逻辑操作  即函数。

val rdd=sc.makeRDD(Array(  ("B","1"), ("B","2") ("B","3") ("A","1") ("A","2"))

rdd.aggregateByKey("")(    (p,v)=>p+" "+v      ,    (v,v) =>v+" "+v       ).collect.foreach(println)

结果如下:

 (A,1 2)

   (B, 1 2 3) 

九:cogroup 会进行shuffer

和join类似,不过join是笛卡尔链接,cogroup是连接到一个元组中。

def cogroup[w] (otherRDD(K,V)):RDD(  K, (Tterable[v]),  Iterable[v]  ))

其中红色的K是两个rdd中的某个key,  第一个迭代器表示第一个rdd中该key的所有元素,第二个迭代器是第二个rdd中该key的所有元素。   可以看到cogroup并不是暴力合并,而是区分了rdd合并成了对应rdd该key的数据。

val rdd1=sc.makeRDD(Array( ("A","1"),("A","2"),("B","1") ,("B","2")   ))

val rdd2=sc.makeRDD(Array( ("A","3"),("A","4"),("B","3") ,("B","4")   ))

val joinRDD=rdd1.cogrop(rdd2)

val resultRDD=joinRDD.map(x=>{

val key=x._1;

val iterable1=x._2;

val iterable2=x_3;

var str1="";

var str2="";

iterable1.foreach(element=>{

str1=str1+element+" ";

}  )

iterable2.foreach(element=>{

str2=str2+element+" ";    }   )

}

(key,(str1),(str2))

     )

resultRDD.collect.foreach(println)

结果如下:

(A,(1,2),(3,4))

(B,(1,2),(3,4))

十:flatMap

“flatMap “函数的一半功能和map函数一样,不过有个要求,传入的函数在处理完后返回值必须是List(应该是Seq),如果结果不是List(Seq),那么将出错。也就是说,传入的函数是有要求的——返回值是Seq才行。这样,每个元素处理后返回一个List,我们得到一个包含List元素的List,flatMap自动将所有的内部list的元素取出来构成一个List返回。

sample:


var li= List(1,2,3,4)
li.flatMap(x => x match {
     case 3 => List(3.1,3.2)
     case _ => x*2
  })

//结果=>
main.scala:4: error: type mismatch;
 found   : Int
 required: scala.collection.GenTraversableOnce[?]
      case _ => x*2
	传入flatMpa的函数返回值必须是List,将x*2改成List(x*2),并且和map函数对比一下,看结果:var li= List(1,2,3,4)
var res = li.flatMap(x => x match {
     case 3 => List(3.1,3.2)
     case _ => List(x*2)
  })
println(res)
li= List(1,2,3,4)
var  res2 = li.map(x => x match {
     case 3 => List(3.1,3.2)
     case _ => x*2
  })
println(res2)

//output=>
List(2, 4, 3.1, 3.2, 8)
List(2, 4, List(3.1, 3.2), 8)
Program exited.

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

我先森

鼓励一个吧,哈哈

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

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

打赏作者

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

抵扣说明:

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

余额充值