1、概述
分类:
Transformations 和Actions 以及 持久化算子
Transformations :
将一个RDD转换成另一个RDD
所有的Transformation都是lazy的,只有发生action是才会触发计算
Action:
这类算子会触发 SparkContext提交作业
一个action算子就是一个job(作业)
问题:
spark官网说这样设置算子会使spark运行地更加的高效,请问这是为什么呢?
答:假设执行一个rdda.map().reduce()的操作,如果作为转换算子map()也触发计算,则肯定得将
结果写出来,降低效率。第二则是由于lineage的关系。
持久化算子:
略
参考:https://blog.csdn.net/pengzonglu7292/article/details/79485271
2、Transformations中的算子
1、map && flatMap
scala> sc.makeRDD(List("hello spark","hello hive")).flatMap(_.split("
")).map((_,1)).reduceByKey(_+_).collect
res1: Array[(String, Int)] = Array((hive,1), (spark,1), (hello,2))
总结:
map 一进一出
flatMap 一进多出
flatMap会将String看成是一个字符数组,不会将Array[String]看成字符数组
2、mapValues && flatMapValues
对k-v型rdd的value进行map和flatMap
操作:
scala> sc.makeRDD(List("hello" -> 1)).mapValues(_+1).collect
res2: Array[(String, Int)] = Array((hello,2))
scala> sc.makeRDD(Array(2 -> "hive on spark")).flatMapValues(_.split(" ")).collect
res3: Array[(Int, String)] = Array((2,hive), (2,on), (2,spark))
3、filter && distinct
filter:对rdd数据过滤
操作:
scala> sc.parallelize(1 to 9).filter(_%2==0).collect
res4: Array[Int] = Array(2, 4, 6, 8)
distinct:会对数据进行去重
scala> sc.makeRDD(List(2,3,2,4)).distinct.collect
res5: Array[Int] = Array(4, 2, 3)
4、sample && glom
sample:对rdd数据进行抽样
第一个参数:是否放回,true放回,false不放回
第二个参数:抽样比例,大概比例,并非精准按比例抽样
第三个参数:种子数,可忽略
操作:
scala> sc.parallelize(1 to 100).sample(true,0.1).collect
res6: Array[Int] = Array(9, 22, 30, 37, 43, 51, 74, 83)
glom: 将一个分区所有数据装入Array中,这样rdd中每一个分区就只包含一个数组
操作:
scala> sc.makeRDD(1 to 10,3).glom().collect()
res7: Array[Array[Int]] = Array(Array(1, 2, 3), Array(4, 5, 6), Array(7, 8, 9, 10))
5、coalesce && repartition
coalesce默认不shuffle,开启shuffle第2个参数设为true
repartition默认shuffle,无第2参数,故无法关闭shuffle
注:
由于增大分区必需开启shuffle,故用repartition增大分区,coalesce减小分区
操作:
scala> sc.makeRDD(1 to 10,2).coalesce(1).partitions.size
res8: Int = 1
scala> sc.makeRDD(1 to 10,2).repartition(3).partitions.size
res9: Int = 3
问题:
shuffle作为一个昂贵的操作,我们一定要尽可能的规避shuffle?
答:
一般情况下尽可能规避shuffle没有错,但在分区数据倾斜的情况下,利用重分区算子开启
shuffle,就能起到平均分区数据的作用。
6、intersection && subtract && union
intersection:返回两个RDD的交集,并且去重。分数区等于max{两个父rdd分区数}
操作:
scala> sc.makeRDD(Array(1,1,3),3).intersection(sc.makeRDD(Array(1,1,4),2)).collect
res10: Array[Int] = Array(1)
subtract:返回两个rdd的差集,不去重。谁调用分区数和谁一致
操作:
scala> sc.makeRDD(Array(1,2,2),2).subtract(sc.makeRDD(Array(1,3,3),3)).collect
res11: Array[Int] = Array(2, 2)
scala> sc.makeRDD(Array(1,3,3),2).subtract(sc.makeRDD(Array(1,2,2),3)).collect
res12: Array[Int] = Array(3, 3)
union:返回两个rdd的并集,不去重。合并后分区数等于两父rdd之和。
操作:
scala> sc.makeRDD(1 to 4,3).union(sc.makeRDD(5 to 10,2)).collect
res13: Array[Int] = Array(5, 6, 7, 8, 9, 10, 1, 2, 3, 4)
7、mapPartitions && mapPartitionsWithIndex
mapPartitions:和map一样,只不过操作的数据是一个分区。在操作数据库时应用广泛
操作:
scala> val a=sc.makeRDD(1 to 5,2).mapPartitions(
| x =>{ //x代表一个分区的所有数据
| var list=List()
| var i=0
| while(x.hasNext){
| i+=x.next() //将每个分区数据相加
| }
| (i::list).iterator
| }
| ).collect
res14: Array[Int] = Array(3, 12)
mapPartitionsWithIndex:与mapPartitions相比,该算子有两个参数,第一个参数为分区索引
操作:
scala> sc.makeRDD(1 to 5,2).mapPartitionsWithIndex(
| (index,x) => {
| var list=List()
| var i=0
| while(x.hasNext){
| i+=x.next()
| }
| ((index,i)::list).iterator
| }
| ).collect
res15: Array[(Int, Int)] = Array((0,3), (1,12))
8、zip && zipPartitions && zipWithIndex &&zipWithUniqueId
zip:两个rdd合并成一个k-v型的rdd,要求两个rdd分区数和对应分区内元素个数一致,否则异常
操作:
scala> sc.makeRDD(1 to 3,2).zip(sc.makeRDD(4 to 6,2)).collect
res16: Array[(Int, Int)] = Array((1,4), (2,5), (3,6))
zipPartitions:将两个rdd合并成一个k-v形式rdd,要求两个rdd分区数一致,否则异常
操作:
略
zipWithIndex:将rdd中的元素和索引号形成键值对,返回一个新的rdd
操作:
scala> sc.makeRDD(1 to 3,2).zipWithIndex().collect #和分区无关
res17: Array[(Int, Long)] = Array((1,0), (2,1), (3,2))
zipWithUniqueId:将rdd中的元素与唯一id形成键值对,返回一个新的rdd
唯一id生成算法:
分区内第一个元素:唯一id为分区号
分区内非首个元素:唯一id为前一个元素id + rdd总共的分区数
操作:
scala> sc.makeRDD(1 to 5,2).zipWithUniqueId().collect
res18: Array[(Int, Long)] = Array((1,0), (2,2), (3,1), (4,3), (5,5))
9、randomSplit
将一个RDD切分成多个RDD,返回一个RDD数组
操作:
scala>val sr = sc.makeRDD(1 to 10).randomSplit(Array(1.0,2.0,3.0))
scala> sr(0).collect
res19: Array[Int] = Array()
scala> sr(1).collect
res20: Array[Int] = Array(3, 6, 10)
scala> sr(2).collect
res21: Array[Int] = Array(1, 2, 4, 5, 7, 8, 9)
解释:
该算子的第一个函数为权重(要求double类型),权重越高的RDD,划分到数据的几率越大。
第二个参数为种子,可以忽略。
10、foldByKey && groupByKey && groupBy
foldByKey:将rdd中的键值对根据key将value折叠
操作:
scala> sc.makeRDD(Array(("hello",1),("hello",2),("spark",2),("spark",0))).foldByKey(0)(_+_).collect
res22: Array[(String, Int)] = Array((spark,2), (hello,3)) //key相加后再加0
scala> sc.makeRDD(Array(("hello",1),("hello",2),("spark",2),("spark",0))).foldByKey(1)(_+_).collect
res23: Array[(String, Int)] = Array((spark,4), (hello,5)) //key相加后再加1
scala> sc.makeRDD(Array(("hello",1),("hello",2),("spark",2), ("spark",0))).foldByKey(0)(_*_).collect
res24: Array[(String, Int)] = Array((spark,0), (hello,0)) //key相乘后再乘0
scala> sc.makeRDD(Array(("hello",1),("hello",2),("spark",2),("spark",0))).foldByKey(1)(_*_).collect
res25: Array[(String, Int)] = Array((spark,0), (hello,2)) //key相乘后再加1
groupByKey:按key分组,value放入集合中,唯一参数可以是分区数或分区函数或不传
操作:
scala> sc.makeRDD(Array(("hello",1),("hello",2),("spark"
,2),("spark",0))).groupByKey().collect
res26: Array[(String, Iterable[Int])] = Array((spark,CompactBuffer(2, 0)),
(hello,CompactBuffer(1, 2)))
groupBy:按分区函数分组,key-value放入集合中
操作:
scala> sc.makeRDD(Array(("hello",1),("hello",2),("spark",2),("spark",0))).groupBy(x
=> x._1).collect
res3: Array[(String, Iterable[(String, Int)])] =
Array((spark,CompactBuffer((spark,2), (spark,0))), (hello,CompactBuffer((hello,1), (hello,2))))
11、reduceByKey && reduceByKeyLocally
reduceByKey:按key对value进行运算,
操作:
scala> sc.makeRDD(Array(("hello",1),("hello",2),("spark",2),("spark",0))).reduceByKey((_+_),2).collect //指定分区数
res27: Array[(String, Int)] = Array((hello,3), (spark,2))
scala> sc.makeRDD(Array(("hello",1),("hello",2),("spark",2),("spark",0))).reduceByKey(_+_).collect
res28: Array[(String, Int)] = Array((spark,2), (hello,3))
scala> sc.makeRDD(Array(("hello",1),("hello",2),("spark",2),("spark",0))).reduceByKey(new org.apache.spark.HashPartitioner(2),(_+_)).collect
res29: Array[(String, Int)] = Array((hello,3), (spark,2)) //指定分区函数
reduceByKeyLocally:按key对value进行运算,返回一个map集合
操作:
scala> sc.makeRDD(Array(("hello",1),("hello",2),("spark",2),
("spark",0))).reduceByKeyLocally(_+_)
res30: scala.collection.Map[String,Int] = Map(spark -> 2, hello -> 3)
12、partitionBy && cogroup
partitionBy:根据传入的分区函数对key-value行的rdd重新进行分区
操作:
scala> sc.makeRDD(Array(("hello",1),("spark",1),("hive",2))).partitionBy(new org.apache.spark.HashPartitioner(3)).partitions.size
res31: Int = 3
cogroup:相当于全外连接,参数可以有多个
操作:
scala> sc.makeRDD(Array((1,"a"),(2,"b"))).cogroup(sc.makeRDD(Array((1,"aa"),(3,"c")))).collect
res32: Array[(Int, (Iterable[String], Iterable[String]))] = Array((1,(CompactBuffer(a),CompactBuffer(aa))), (2,(CompactBuffer(b),CompactBuffer())), (3,(CompactBuffer(),CompactBuffer(c))))
可以2个以上的rdd join
13、join && leftOuterJoin && rightOuterJoin && fullOuterJoin
scala> sc.makeRDD(Array((1,"zhang3"),(3,"li4"))).join((sc.makeRDD(Array((1,"19"),(2,20))))).collect
res33: Array[(Int, (String, Any))] = Array((1,(zhang3,19)))
scala> sc.makeRDD(Array((1,"zhang3"),(3,"li4"))).leftOuterJoin((sc.makeRDD(Array((1,"19"),(2,20))))).collect
res34: Array[(Int, (String, Option[Any]))] = Array((1,(zhang3,Some(19))), (3,(li4,None)))
scala> sc.makeRDD(Array((1,"zhang3"),(3,"li4"))).rightOuterJoin((sc.makeRDD(Array((1,"19"),(2,20))))).collect
res35: Array[(Int, (Option[String], Any))] = Array((1,(Some(zhang3),19)), (2,(None,20)))
scala> sc.makeRDD(Array((1,"zhang3"),(3,"li4"))).fullOuterJoin((sc.makeRDD(Array((1,"19"),(2,20))))).collect
res36: Array[(Int, (Option[String], Option[Any]))] = Array((1,(Some(zhang3),Some(19))), (2,(None,Some(20))), (3,(Some(li4),None)))
14、sortBy && sortByKey
sortBy:
参数1:排序函数 参数2:排序方式,默认true升序 参数3:排序后的分区数
只有第一个参数是必须要传入的,底层调用了sortByKey
操作:
scala> sc.makeRDD(Array(2,3,1,4,5)).sortBy(x=> x).collect
res7: Array[Int] = Array(1, 2, 3, 4, 5)
scala> sc.makeRDD(Array(2,3,1,4,5)).sortBy(x=> x,false).collect
res8: Array[Int] = Array(5, 4, 3, 2, 1)
scala> sc.makeRDD(Array(2,3,1,4,5)).sortBy(x=> x,false,3).collect
res9: Array[Int] = Array(5, 4, 3, 2, 1)
sortByKey:
按k-v型rdd的key排序
参数1:排序方式,默认true升序 参数2:排序后的分区数
操作:
scala> sc.makeRDD(Array((2,"lily"),(1,"lucy"),(3,"cindy"))).sortByKey().collect
res10: Array[(Int, String)] = Array((1,lucy), (2,lily), (3,cindy))
scala> sc.makeRDD(Array((2,"lily"),(1,"lucy"),(3,"cindy"))).sortByKey(false,2).collect
res11: Array[(Int, String)] = Array((3,cindy), (2,lily), (1,lucy))
sortBy && sortByKey:
虽然他们是transformations算子,但他们仍然会触发作业。
3、Actions算子
1、first && take && top && takeOrdered
first:返回一个元素
scala> sc.makeRDD(1 to 10).first()
res37: Int = 1
take:返回前n个元素
scala> sc.makeRDD(1 to 10).take(2)
res38: Array[Int] = Array(1, 2)
top:按照默认顺序(降序)取前n个元素
scala> sc.makeRDD(1 to 10).top(2)
res39: Array[Int] = Array(10, 9)
takeOrdered:按照升序取前n个元素
scala> sc.makeRDD(1 to 10).takeOrdered(2)
res40: Array[Int] = Array(1, 2)
2、collect && count && reduce
collect:将一个rdd转成数组,返回到driver端
scala> sc.makeRDD(List(1,2,3,4)).collect
res41: Array[Int] = Array(1, 2, 3, 4)
count:返回rdd中元素的数量
scala> sc.makeRDD(List(1,2,3,4)).count
res42: Long = 4
reduce:对rdd中的元素进行二元计算
scala> sc.makeRDD(1 to 10).reduce(_+_)
res43: Int = 55
3、foreach && lookup && foreachPartition
foreach:打印rdd中的元素
scala> sc.makeRDD(1 to 10).foreach(print)
67891012345
lookup:给出key值,返回value
scala> sc.makeRDD(Array((1,"zhang3"),(3,"li4"))).lookup(1)
res44: Seq[String] = WrappedArray(zhang3)
foreachPartition:
4、countByKey
countByKey:返回k-v中每个key出现的次数
scala> sc.makeRDD(Array(("A",0),("A",2),("B",1),("B",2),("B",3))).countByKey
res45: scala.collection.Map[String,Long] = Map(A -> 2, B -> 3)
5、saveAsTextFile && saveAsObjectFile && saveAsHadoopFile && saveAsHadoopDataset
这几个算子都可以指定特定的对应的压缩格式codec
saveAsTextFile:
将rdd数据保存在文件系统中,一个分区对应一个文件
操作:
scala> sc.parallelize(1 to 10,2).saveAsTextFile("hdfs://Linux001/data/")
[root@Linux001 ~]# hdfs dfs -ls /data
Found 3 items
-rw-r--r-- 1 root supergroup 0 2019-05-25 11:21 /data/a.txt/_SUCCESS
-rw-r--r-- 1 root supergroup 4 2019-05-25 11:21 /data/a.txt/part-00000
-rw-r--r-- 1 root supergroup 6 2019-05-25 11:21 /data/a.txt/part-00001
[root@Linux001 ~]# hdfs dfs -cat /data/part-00000
1
2
[root@Linux001 ~]# hdfs dfs -cat /data/part-00001
3
4
5
saveAsObjectFile:将rdd数据保存在文件系统中,一个分区对应一个文件
操作:
和saveAsTextFile一样,略