Spark有许多常用算子,这里结合几篇文章做一下备份,方便自己后面快速查阅。本篇文章主要分析每个算子的应用方法,后面写到RDD、DataSet和DataFrame区别的时候会从算子的数据类型转变的进行进一步分析。
Transformation
- map
map的输入变换应用于RDD中的所有元素。
scala> val array = Array(1,2,3,4)
array:Array[Int] = Array(1, 2, 3, 4)
scala>array.map(x=>(x,"spark"))
res0:Array[(Int,String)]=Array((1,"spark"),(2,"spark"),(3,"spark"),(4,"spark"))
- flatmap
flatmap与map的区别在于flat,flat有扁平的意思。
scala> val arr = Array("spark,datas","hadoop,datah","python,datap")
arr:Array[String] = Array(spark,datas,hadoop,datah,python,datap)
scala> arr.flatmap(x=>x.split(","))
res:Array[String] = Array(spark,datas,hadoop,datah,python,datap)
- mapValues
scala> val list = List("hadoop","spark","hive","spark")
scala> val rdd = sc.parallelize(list)
scala> val pairRdd = rdd.map(x => (x,1))
scala> pairRdd.mapValues(_+1).collect.foreach(println)
res1:(hadoop,2),(spark,2),(hive,2),(spark,2)
- mapPartitions
mapPartitions与map的区别也在于partitions(分区),map每处理一个元素就需要调用一次函数,而mapPartition则是每个分区调用一次。有很多博客说在数据量很低的时候可以有效提升性能,有的也说用这个算子比较危险容易出现OOM问题。特别是对于一些容易产生数据激增的业务场景里面。但是对于一些写数据库的场景中具有很好的性能优化。因为对于分布式场景中,连接数据库的操作必须在算子内部才能被executor执行,这个时候每个partition只需要建立一次连接就好了。至于数据量上的区别还有待于我自己测试后进行说明! - filter(func)
过滤操作,根据func的条件过滤RDD中元素。返回一个新的RDD
scala> val a = Array(1,2,3,4,5,6,7)
a:Array[Int] = Array(1, 2, 3, 4, 5, 6, 7)
scala> a.filter(x=>x%2 == 0)
res8:Array[Int] = Array(2,4,6,8)
- union(rdd)
合并两个数据集,不去重!!!
scala> val aa = Array(1,2,3)
aa: Array[Int] = Array(1, 2, 3)
scala> val bb = Array(3,4,5)
bb:Array[Int] = Array(3, 4, 5)
scala> aa.union(bb)
res:Array[Int] = Array(1,2,3,3,4,5)
- instersect
两个数据集求交集,并去重
scala> val aa = Array(1,2,3)
aa: Array[Int] = Array(1, 2, 3)
scala> val bb = Array(3,4,5)
bb:Array[Int] = Array(3, 4, 5)
scala> aa.instersect(bb)
res:Array[Int] = Array(3)
- sample(withReplacement,fraction,seed)
对数据集采样,withReplacement:表示是否放回采样;fraction:采样比例;seed:随机数种子 - groupByKey([numTasks])
pairRDD或(k,v)调用。返回一个(k,Iterable)。主要作用是将相同的key的值分配到一个集合序列中,当然顺序不一定。但是,需要将键值加载到内存中,如果一个键对应的值过多可能会出现OOM问题。先看例子:
# 计算单词的个数
scala> val words = Array("one","two","two","three","three","three")
scala> val wordPairRDD = sc.parallelize(words).map(word => (word, 1))
res:Array[(String,Int)] = Array[(one,1),(two,1),(two,1),(three,1),(three,1),(three,1)]
scala> val wordGroup = wordPairRDD.groupByKey()
groupPair:RDD[(String,Iterable[Int])] = ShuffledRDD[4] at groupByKey
scala>val a = wordGroup.collect()
res:Array[(String,Interable[Int])] = Array[("one",CompactBuffer(1)),
("two",CompactBuffer(1,1)),("three",CompactBuffer(1,1,1))]
# 这里访问方式注意一下,key是一组,value是一组。每一组里面可能也有多个元素。所以会有
# 先确定组再内部访问的方法!!!
scala>a.map(t=>(t._1,t._2.sum)).collect()
这里在补充一个例子,一般在工程中都是这样使用的。
注:1、groupBy(function)——>与groupByKey的区别在于,groupByKey是无参数的所以调用的结构本来就是key-value结构的。而groupBy中根据传入的函数来进行分组。举例来说:
scala> val a = List(("a",2),("a",3),("d",333),("c",3),("c",4))
scala> val b = a.groupBy(_._1)
b: scala.collection.immutable.Map[String,List[(String, Int)]] =
Map(d -> List((d,333)), a -> List((a,2), (a,3)), c -> List((c,3), (c,4)))
scala> val c = b.mapValues(r => {r.map(r => {r._2})})
scala> println(c)
c: scala.collection.immutable.Map[String,List[Int]] = Map(d -> List(333), a -> List(2, 3), c -> List(3, 4))
- reduceByKey
reduceByKey的作用是聚合,groupByKey的作用是分组。上文例子中也可以使用reduceByKey来解决。解决方案如下所示:
val wordCount = wordPairsRDD.reduceByKey(_ + _).collect()
这个是根据key,对迭代器中的数值进行迭代累加。
- 迭代器(Iterator)