Spark - RDD -转换算子
Spark的分区号是从0开始的
flatMap:扁平化
将一个整体拆分成一个一个的个体来使用
val rdd: RDD[List[Int]] = sc.makeRDD(
List(List(1, 2), List(3, 4)
)
)
val rdd2: RDD[Int] = rdd.flatMap(
list => list
)
rdd2.collect().foreach(println)
Groupby:一个组只能在一个分区中
分组后可能会导致不同分区的数据不均衡
默认情况下,分组前和分组后的分区数是不变的
所有的shuffle都有改变分区的能力
如果一个算子能改变分区,90%是有shuffle的
val rdd: RDD[String] = sc.makeRDD(List("hello" , "hadoop" , "hive" , "scala" , "sqoop" , "spark"))
val groups: RDD[(String, Iterable[String])] = rdd.groupBy(_.substring(0,1),2)
groups.collect().foreach(println)
Filter:filter算子会根据指定规则对数据集中的每一条数据进行判断
如果想要数据保留,那么结果返回true,如果数据不要,返回false
scala集合中的filter考虑的是单点操作
sparkRDD的filter考虑分区数据的均衡
val rdd: RDD[Int] = sc.makeRDD(List(1,2,3,4))
rdd.filter(_%2==1).collect().foreach(println)
Sample:用于在数据集中随机抽取数据
分两种情况
一、抽取后不放回 false 如果第一个参数为false,第二个参数表示每个数据被抽取的几率, 取值为0 ~ 1之间
二、抽取后放回 true 如果第一个参数为true,第二个参数表示每个数据预期被抽取的数据, 取值大于1
sample方法第三个参数seed : 随机数种子
val rdd: RDD[Int] = sc.makeRDD(List(1,2,3,4,5,6,7,8,9,10))
val rdd1: RDD[Int] = rdd.sample(false,.2)
//val rdd2: RDD[Int] = rdd.sample(true,3)
//rdd2.collect().foreach(println)
rdd1.collect().foreach(println)
Coalesce:改变分区数量
第一个参数改变分区数
第二个参数为是否用shuffle,默认为false
(如果不适用shuffle的场合下,分区数量不能修改为比之前大的值,因为数据不会打乱重新组合)
val rdd: RDD[Int] = sc.makeRDD(List(1,2,3,4,5,6),6)
val rdd1: RDD[Int] = rdd.coalesce(3)
rdd1.collect().foreach(println)
rdd1.saveAsTextFile("output")
Repartition:底层其实也是coalesce实现的,默认是启用shuffle,无论是将分区数多的RDD转换为分区数少的RDD,还是将分区数少的RDD转换为分区数多的RDD,repartition操作都可以完成,因为无论如何都会经shuffle过程
val rdd = sc.makeRDD(List(1,2,3,4,5,6), 3)
//coalesce 缩减分区
val rdd1: RDD[Int] = rdd.coalesce(2)
//repartition 扩大分区 - shuffle 的coalesce
val rdd2: RDD[Int] = rdd.repartition(4)
Zip:不同类型的两个数据集无法实现交集并集,差集的操作
不同类型的两个数据集可以实现拉链
//并集 1,2,3,4,5,6
println(rdd1.union(rdd2).collect().mkString(","))
//交集 3,4
println(rdd1.intersection(rdd2).collect().mkString(","))
//差集 1,2
println(rdd1.subtract(rdd2).collect().mkString(","))
//拉链zip
println(rdd1.zip(rdd2).collect().mkString(","))
Partitionby:partitionby算子根据指定规则对数据进行重新分区
partitionby算子强调的是分区数据得变化,而不是分区数量得变化
RDD一定没有partitionby方法,是通过隐式转换调用得方法
partitonby处理的数据类型一定是KV类型
partitionby算子需要传递分区器对象
Spark中默认得分区器是Hashpartitioner
val rdd: RDD[Int] = sc.makeRDD(List(1,2,3,4),2)
rdd.saveAsTextFile("output1")
val rdd1: RDD[(Int, Int)] = rdd.map((_,1))
val rdd2: RDD[(Int, Int)] = rdd1.partitionBy(new HashPartitioner(2))
rdd2.saveAsTextFile("output2")
ReduceByKey:将相同数据key的value聚合在一起
val rdd : RDD[(String, Int)] = sc.makeRDD(
List(
("a",1), ("b", 2), ("a", 3)
)
)
rdd.reduceByKey(_+_,2).collect().foreach(println)
GroupByKey: groupByKey算子根据数据的key对数据的value进行分组
val rdd : RDD[(String, Int)] = sc.makeRDD(
List(
("a",1), ("b", 2), ("a", 3)
)
)
val value: RDD[(String, Iterable[Int])] = rdd.groupByKey()
value.collect().foreach(println)
reduceByKey和groupByKey的区别?
从shuffle的角度:reduceByKey和groupByKey都存在shuffle的操作,但是reduceByKey可以在shuffle前对分区内相同key的数据进行预聚合(combine)功能,这样会减少落盘的数据量,而groupByKey只是进行分组,不存在数据量减少的问题,reduceByKey性能比较高。
从功能的角度:reduceByKey其实包含分组和聚合的功能。groupByKey只能分组,不能聚合,所以在分组聚合的场合下,推荐使用reduceByKey,如果仅仅是分组而不需要聚合。那么还是只能使用groupByKey
AggregateByKey: 存在函数柯里化操作,存在多个参数
第一个参数列表表示计算初始值
第二个参数列表有两个参数
第一个参数为分区内的计算规则
第二个参数为分区间的计算规则
val rdd = sc.makeRDD(
List(
("a", 1), ("a", 2), ("b", 3),
("a", 4), ("b", 5), ("b", 6)
),2
)
// a,-1 b,-8
/* rdd.aggregateByKey(0)(
(x,y)=>x+y,
(x,y)=>x-y
).collect().foreach(println)*/
//a,4 b,5
rdd.aggregateByKey(5)(
(x,y)=>math.max(x,y),
(x,y)=>x+y
).collect().foreach(println)
FlodByKey:如果aggregateByKey的分区内计算规则和分区间的计算规则一样,那么可以用flodbykey来代替
val rdd = sc.makeRDD(
List(
("a", 1), ("a", 2), ("b", 3),
("a", 4), ("b", 5), ("b", 6)
),2
)
rdd.foldByKey(0)(_+_).collect().foreach(println)
CombineByKey:第一个参数表示初始值的转换
第二个参数为分区内的计算规则
第三个参数为分区间的计算规则
val rdd = sc.makeRDD(
List(
("a", 1), ("a", 2), ("b", 3),
("a", 4), ("b", 5), ("b", 6)
),2
)
rdd.combineByKey(
num=>num+2,
(x:Int,y:Int)=>{
x+y
},
(x:Int,y:Int)=>{
x+y
}
).collect().foreach(println)
reduceByKey、foldByKey、aggregateByKey、combineByKey的区别?
reduceByKey: 相同key的第一个数据不进行任何计算,分区内和分区间计算规则相同
foldByKey: 相同key的第一个数据和初始值进行分区内计算,分区内和分区间计算规则相同
AggregateByKey:相同key的第一个数据和初始值进行分区内计算,分区内和分区间计算规则可以不相同
CombineByKey:当计算时,发现数据结构不满足要求时,可以让第一个数据转换结构。分区内和分区间计算规则不相同。
SortByKey:根据key进行排序
val rdd = sc.makeRDD(
List(
(2,1),(3,1),(1,1), (1,3),(1,2)
),2
)
rdd.sortByKey(true).collect().foreach(println)
Join:两个数据集的连接条件为相同的key进行连接
只要数据中有相同的key,就都会连接,不考虑数据量的问题
join可能会产生大量的数据,如果业务中存在shuffle,性能非常差,则不推荐使用
val rdd1 = sc.makeRDD(
List(
("a", 1), ("b", 2), ("c", 3)
)
)
val rdd2 = sc.makeRDD(
List(
("a", 5), ("c", 6),("a", 4)
)
)
rdd1.join(rdd2).collect().foreach(println)
CoGroup:在类型为(K,V)和(K,W)的RDD上调用,返回一个(K,(Iterable,Iterable))类型的RDD
val rdd1 = sc.makeRDD(
List(
("a", 1), ("b", 2), ("c", 3)
)
)
val rdd2 = sc.makeRDD(
List(
("a", 5), ("c", 6),("a", 4)
)
)
rdd1.cogroup(rdd2).collect().foreach(println)
运行结果:
(a,(CompactBuffer(1),CompactBuffer(5, 4)))
(b,(CompactBuffer(2),CompactBuffer()))
(c,(CompactBuffer(3),CompactBuffer(6)))