Spark总结之RDD(五)
1. 背景
- Spark作为分布式处理引擎,针对数据分布式处理做了很多抽象,这样在API使用上,可以屏蔽更多代码细节,使得编程更加遍历
- Spark抽象出的有RDD、dataSet、dataFrame、DStream等
- RDD从行为上可以划分transformation、action、既不是transformation也不是action三大种类型
- RDD从api接口设计上,可以分为底层RDD,高层级RDD,高层级RDD一般是使用底层的RDD来实现,稍后源码简洁会做展示。
- RDD只是一个抽象,本身并不存储数据,只是记录所需要处理数据来源,所需要对数据做的操作。
2. RDD类型
2.1 sortBy
- 顾名思义,这个是根据传入的规则进行排序
val conf: SparkConf = new SparkConf()
.setAppName(SortByTest.getClass.getSimpleName)
.setMaster("local[*]")
val sc = new SparkContext(conf)
val list = List(2, 1, 4, 6, -10)
val rdd1: RDD[Int] = sc.parallelize(list, 2)
// 根据元素本身排序,默认是升序
val res1 = rdd1.sortBy(x => x)
// x => x.toString 这是排序规则
val res2: RDD[Int] = rdd1.sortBy(x => x.toString)
println(res1.collect().toBuffer)
println("================")
println(res2.collect().toBuffer)
sc.stop()
运算结果
ArrayBuffer(-10, 1, 2, 4, 6)
================
ArrayBuffer(-10, 1, 2, 4, 6)
- 源码
可以看出,本身是先调用keyBy,将自身转换为key value形式,不过这里key value都是本身
然后使用sortByKey进行排序
最后将values取出,就是排序后的结果
2.2 sortByKey
- 顾名思义,这里是按照key进行排序,可以指定排序规则。
val conf: SparkConf = new SparkConf()
.setAppName(SortByKeyTest.getClass.getSimpleName)
.setMaster("local[*]")
val sc = new SparkContext(conf)
val list = List((3, "haha"), (3, "xixi"), (4, "mimi"), (-3, "yuyu"))
val rdd1: RDD[(Int, String)] = sc.makeRDD(list)
// 默认就是升序的,可以选择传入排序是升序还是降序
val res: RDD[(Int, String)] = rdd1.sortByKey(false)
println(res.collect().toBuffer)
sc.stop()
运算结果
ArrayBuffer((4,mimi), (3,haha), (3,xixi), (-3,yuyu))
- 源码
从上述源码可以看出,sortByKey最终是调用了ShuffledRDD
也就是说,这里出现了shuffle操作。
注意代码中有一个RangePartitioner,这也是一个分区器,不过是将数据按照range范围划分到一起。
分区器本身就是分区规则的一种体现
这里代码本质就是,数据需要按照key进行排序,但是整体排序性能消耗太高,所以将数据按照范围,每个区相同范围数据划分到一起。每个区进行排序,最后结果是每个区按照升序或降序进行归并,这样得出的结果就是整体有序的。
这种代码实现思路,很好体现了分布式计算的思想。就是尽可能把工作划分到不同的节点上去处理,最终结果做一下聚合就是最后结果。
MapReduce也是这种思想的实践成果,spark也很大借鉴了Mapreduce的思想
Rangepartitioner源码
上述可以看出,RangePartitioner得到分区数,是会先通过一次采样,根据采样得到分区数量,所以可能会不是很准确。
同时后续传入的数据划分到哪个分区是根据采样时得到的区间来判定的。这样就可以保证某一个范围内的数据都会落到一个分区中。
一定一定注意,所谓分区器其实就是数据划分规则的体现,这里是按照范围分区
2.3 keyBy
这里是把数据转换为key value形式,不过key的生成是基于原有数据进行转换得来,value就是原有的值。
val conf: SparkConf = new SparkConf()
.setAppName(keybyTest.getClass.getSimpleName)
.setMaster("local[*]")
val sc = new SparkContext(conf)
val list = List(2, 1, 4, 6, -10)
val rdd1: RDD[Int] = sc.makeRDD(list)
val res: RDD[(String, Int)] = rdd1.keyBy(x => "key:" + x)
println(res.collect().toBuffer)
sc.stop()
运算结果
ArrayBuffer((key:2,2), (key:1,1), (key:4,4), (key:6,6), (key:-10,-10))
源码
本质就是使用map做了一次转化。
2.4 collect
- 注意,我们的RDD只是一个抽象,具体代码是要放到driver或者executor中以task形式运行的
- collect就是将executor上的数据搜集回driver端
val conf: SparkConf = new SparkConf()
.setAppName