目录
摘要
关于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.