Spark总结之RDD(四)
1. 背景
- Spark针对RDD的整体设计思想很多地方和MapReduce相似,但又做了很多优化.
- Spark整体API设计针对分布式数据处理做了很多优化,并且做了更高层级的抽象,API使用更加简单便捷.例如RDD\DataSet\DataFrame\DStream等
- 本文主要关于RDD的介绍,RDD类型较多,大的分类是Transformation和Action类型,但具体的RDD中又可以进一步细分为基础的RDD和在基础RDD之上的高级RDD(内部调用基础RDD以及各种操作)
- 好的API设计确实是使用便利,而把复杂的内部实现屏蔽起来.并且会对API接口做分层处理,Spark的RDD的API设计就很好体现了这一点,后续源码会有提及.
2. RDD类型
2.1 cogroup
顾名思义,协分组,可以看成是将多个RDD聚合然后根据key进行划分.
val conf: SparkConf = new SparkConf().setAppName(classOf[CogroupTest].getName)
conf.setMaster("spark://linux101:7077")
val sc = new SparkContext(conf)
// 创建rdd
val rdd1: RDD[(String, Int)] = sc.makeRDD(List(("tome", 1), ("tome", 2), ("jim", 1), ("tony", 1), ("sanny", 1)), 2)
val rdd2: RDD[(String, Int)] = sc.makeRDD(List(("tome", 3), ("tome", 1), ("jim", 4), ("tony", 6), ("sanny", 1)), 2)
// cogroup,将多个rdd联合起来,进行groupByKey的操作。
// 注意这种方式面对大数据场景,由于没有reduceByKey这种提前局部聚合,效率较低
// 使用union,然后reduceByKey相对更加高效一些
val res: RDD[(String, (Iterable[Int], Iterable[Int]))] = rdd1.cogroup(rdd2)
println(res.collect().toBuffer)
Thread.sleep(10000000)
运算结果
ArrayBuffer((jim,(CompactBuffer(1),CompactBuffer(4))), (tony,(CompactBuffer(1),CompactBuffer(6))), (sanny,(CompactBuffer(1),CompactBuffer(1))), (tome,(CompactBuffer(1, 2),CompactBuffer(3, 1))))
可以看出,最后的运算结果,是2个RDD中相同的key,value是2者合并起来的结果集合
实际cogroup的源码如下
注意有多参数的方法重载
最终实现这里,可以看到,就是创建了一个CoGroupedRDD,然后这个RDD使用mapValues对内部的数据做处理
处理逻辑就是当前调用cogroup的RDD的values,以及作为参数的RDD的values,其中的values转换为Iterable
返回值最终是RDD[(K, (Iterable[V], Iterable[W]))] ,可以看出最后生成的是对偶数组,key就是2个RDD中的key,value就是2个RDD的value对应数据形成的迭代器,2个迭代器组合而成的对偶数组.
所以整体可以把cogoup看做是把2个或多个RDD相同key的数据抽离出来,放在一起,但结果是以Tuple形式,Tuple中就是不同RDD的values形成的迭代器.
cogroup是产生了shuffle的
http://windows:4040/jobs/job/?id=0 web页面查看信息
上述可以看出,这里有3个stage, 每个stage是2个分区,并行处理2个task
注意,如果是需要对多个RDD的数据进行处理,可以先union,再局部聚合reduceByKey,这样更加高效.不过数据量少时,使用cogroup性能耗时上不会有太大差异
注意,shuffle的本质和英文名字类似,洗牌.Saprk和MapReduce的shuffle一样,都是把数据打散
数据打散就意味着需要有一个规则将数据划分到不同的区域去,一般是分区器来做这个事情.默认一般是hashPartitioner
实际shuffle分为2个阶段,shuffle write,shuffle read.也就是shuffle前期,数据从内存溢出写到磁盘,然后下游需要拉取这些写道磁盘的数据
shuffle和是否网络传输无关因为上下游有可能在同一个计算机节点上,直接本地通信即可.
shuffle会产生对应的map task
2.2 intersection
顾名思义,就是求RDD之间的交集,隐含着的条件就是2个RDD的数据类型需要一致.(是指RDD产生出来的数据类型需要一致)
val conf: SparkConf = new SparkConf()
.setAppName("IntersectionTest")
.setMaster("local[*]")
val sc = new SparkContext(conf)
// 交集
val rdd1: RDD[Int] = sc.makeRDD(List(2, 3, 4, 5), 2)
val rdd2: RDD[Int] = sc.makeRDD(List( 4, 5, 6, 7), 2)
// 就跟集合操作一样,交集就是求出2个集合中共同拥有的元素
// 所以隐含着一个条件,数据类型需要一样,因为需要2者之间可以对比和判断
val res: RDD[Int] = rdd1.intersection(rdd2