RDD中的所有转换都是延迟加载的,也就是说,它们并不会直接计算结果。相反的,它们只是记住这些应用到基础数据集(例如一个文件)上的转换动作。只有当发生一个要求返回结果给Driver的动作时,这些转换才会真正运行。这种设计让Spark更加有效率地运行。
也就是说Transformation算子,spark程序没有计算,遇到action算子开始计算

Transformation

单value结构

map(func)

返回一个新的RDD,该RDD由每一个输入元素经过func函数转换后组成

rdd.map((_,1))

filter(func)

返回一个新的RDD,该RDD由经过func函数计算后返回值为true的输入元素组成

x.filter(x % 2 == 0)

flatMap(func)

类似于map,但是每一个输入元素可以被映射为0或多个输出元素

rdd.flatMap(_.split(" "))

mapPartitions(func)

类似于map,但独立地在RDD的每一个分片上运行,因此在类型为T的RDD上运行时,func的函数类型必须是Iterator[T] => Iterator[U]

rdd.mapPartitions(x => x.map((_,1)))

mapPartitionsWithIndex(func)

类似于mapPartitions,但func带有一个整数参数表示分片的索引值,因此在类型为T的RDD上运行时,func的函数类型必须是(Int, Iterator[T]) => Iterator[U]

rdd.mapPartitionsWithIndex((index,items)=>(items.map((index,_))))

sample(withReplacement, fraction, seed)

根据fraction指定的比例对数据进行采样,可以选择是否使用随机数进行替换,seed用于指定随机数生成器种子

glom

将每一个分区形成一个数组,形成新的RDD[Array[T]]

val value: RDD[Array[Int]] = rdd.glom()

groupBy

按照传入函数的返回值进行分组。将相同的key对应的值放入一个迭代器

val groupBy: RDD[(Int, Iterable[Int])] = rdd.groupBy(_%3)

distinct([numTasks]))

对源RDD进行去重后返回一个新的RDD

rdd.distinct()

coalesce(numPartitions)

重新分区

rdd.coalesce(3)

repartition(numPartitions)

重新分区

rdd.repartition(2)

sortBy

rdd.sortBy(x => x%3)

pipe

管道,针对每个分区,都执行一个shell脚本,返回输出的RDD,注意:脚本需要放在Worker节点可以访问到的位置

rdd.pipe("/opt/module/spark/pipe.sh")

双value结构

union(otherDataset)

两个rdd的并集

rdd1.union(rdd2)

intersection(otherDataset)

两个rdd的交集

rdd1.intersection(rdd2)

subtract

两个rdd的差集

rdd.subtract(rdd1)

zip

将两个RDD组合成Key/Value形式的RDD,这但是两个RDD的partition数量以及元素数量都相同,否则会抛出异常

cartesian

笛卡尔积,尽量避免使用

rdd1.cartesian(rdd2).

val rdd1 = sc.parallelize(Array(1,2,3),3)
val rdd2 = sc.parallelize(Array("a","b","c"),3)
rdd1.zip(rdd2).collect

res1: Array[(Int, String)] = Array((1,a), (2,b), (3,c))
//foreachPartition一般对分区的一个元素和items操作,一般用来数据库操作
    key2CountActionRDD.foreachPartition{
      items =>
        val statArray = new ArrayBuffer[AdStat]()
        for (item <- items) {
          val keySplited: Array[String] = item._1.split("_")
          val date = keySplited(0)
          val province = keySplited(1)
          val city = keySplited(2)
          val adid = keySplited(3).toLong

          val clickCount = item._2
          statArray += AdStat(date, province, city, adid, clickCount)
        }
        AdStatDAO.updateBatch(statArray.toArray)
    }

k-v结构

partitionBy

按照k进行分区

rdd.partitionBy(new org.apache.spark.HashPartitioner(2))

groupByKey([numTasks])

在一个(K,V)的RDD上调用,返回一个(K, Iterator[V])的RDD

val groupByKey: RDD[(Int, Iterable[Int])] = kvRdd.groupByKey()

sortByKey([ascending], [numTasks])

只能按照k排序,K必须实现Ordered接口

rdd.sortByKey(true)

mapValues

针对于(K,V)形式的类型只对V进行操作

rdd3.mapValues("@"+_) //对每个value前面都加上字符串@

join

在类型为(K,V)和(K,W)的RDD上调用,返回一个相同key对应的所有元素对在一起的(K,(V,W))的RDD

cogroup

在类型为(K,V)和(K,W)的RDD上调用,返回一个(K,(Iterable,Iterable))类型的RDD

reduceByKey(func, [numTasks])

在一个(K,V)的RDD上调用,返回一个(K,V)的RDD,使用指定的reduce函数,将相同key的值聚合到一起,与groupByKey类似,reduce任务的个数可以通过第二个可选的参数来设置

val reduceByKey = kvrdd.reduceByKey(+)

aggregateByKey(zeroValue)(seqOp, combOp, [numTasks])

foldByKey

combineByKey

join
> val rdd = sc.parallelize(Array((1,"a"),(2,"b"),(3,"c")))
> val rdd1 = sc.parallelize(Array((1,4),(2,5),(3,6)))
> rdd.join(rdd1).collect()
res13: Array[(Int, (String, Int))] = Array((1,(a,4)), (2,(b,5)), (3,(c,6)))
cogroup
val rdd = sc.parallelize(Array((1,"a"),(2,"b"),(3,"c")))
val rdd1 = sc.parallelize(Array((1,4),(2,5),(3,6)))
rdd.cogroup(rdd1).collect()
res14: Array[(Int, (Iterable[String], Iterable[Int]))] = Array((1,(CompactBuffer(a),CompactBuffer(4))), (2,(CompactBuffer(b),CompactBuffer(5))), (3,(CompactBuffer(c),CompactBuffer(6))))
aggregateByKey

参数:(zeroValue:U,[partitioner: Partitioner]) (seqOp: (U, V) => U,combOp: (U, U) => U)

  1. 作用:在kv对的RDD中,,按key将value进行分组合并,合并时,将每个value和初始值作为seq函数的参数,进行计算,返回的结果作为一个新的kv对,然后再将结果按照key进行合并,最后将每个分组的value传递给combine函数进行计算(先将前两个value进行计算,将返回结果和下一个value传给combine函数,以此类推),将key与计算结果作为一个新的kv对输出。
  2. 参数描述:
    (1)zeroValue:给每一个分区中的每一个key一个初始值;
    (2)seqOp:函数用于在每一个分区中用初始值逐步迭代value;
    (3)combOp:函数用于合并每个分区中的结果。
  3. 经典需求:创建一个pairRDD,取出每个分区相同key对应值的最大值,然后相加
> val rdd = sc.parallelize(List(("a",3),("a",2),("c",4),("b",3),("c",6),("c",8)),2)
> val agg = rdd.aggregateByKey(0)(math.max(_,_),_+_)
> agg.collect()
res0: Array[(String, Int)] = Array((b,3), (a,3), (c,12))
foldByKey

参数:(zeroValue: V)(func: (V, V) => V): RDD[(K, V)]

  1. 作用:aggregateByKey的简化操作,seqop和combop相同
  2. 需求:创建一个pairRDD,计算相同key对应值的相加结果
> val rdd = sc.parallelize(List((1,3),(1,2),(1,4),(2,3),(3,6),(3,8)),3)
> val agg = rdd.foldByKey(0)(_+_)
> agg.collect()
res61: Array[(Int, Int)] = Array((3,14), (1,9), (2,3))
combineByKey

参数:(createCombiner: V => C, mergeValue: (C, V) => C, mergeCombiners: (C, C) => C)

  1. 作用:对相同K,把V合并成一个集合。
  2. 参数描述:
    (1)createCombiner: combineByKey() 会遍历分区中的所有元素,因此每个元素的键要么还没有遇到过,要么就和之前的某个元素的键相同。如果这是一个新的元素,combineByKey()会使用一个叫作createCombiner()的函数来创建那个键对应的累加器的初始值
    (2)mergeValue: 如果这是一个在处理当前分区之前已经遇到的键,它会使用mergeValue()方法将该键的累加器对应的当前值与这个新的值进行合并
    (3)mergeCombiners: 由于每个分区都是独立处理的, 因此对于同一个键可以有多个累加器。如果有两个或者更多的分区都有对应同一个键的累加器, 就需要使用用户提供的 mergeCombiners() 方法将各个分区的结果进行合并。
  3. 经典需求:创建一个pairRDD,根据key计算每种key的均值。(先计算每个key出现的次数以及可以对应值的总和,再相除得到结果)
val input = sc.sparkContext.parallelize(Array(("a", 88), ("b", 95), ("a", 91), ("b", 93), ("a", 95), ("b", 98)), 2)
val combine: RDD[(String, (Int, Int))] = input.combineByKey(
  (_, 1),
  (acc: (Int, Int), v) => (acc._1 + v, acc._2 + 1),
  (acc1: (Int, Int), acc2: (Int, Int)) => (acc1._1 + acc2._1, acc1._2 + acc2._2))
println(combine.collect().mkString(""))//(b,(286,3))(a,(274,3))

val res: RDD[(String, Double)] = combine.map {
  case (key, value) =>
    (key, value._1 / value._2.toDouble)
}
println(res.collect().mkString(""))//(b,95.33333333333333)(a,91.33333333333333)

Action

算子

介绍

reduce(func)

通过func函数聚集RDD中的所有元素,这个功能必须是可交换且可并联的

collect()

在驱动程序中,以数组的形式返回数据集的所有元素

count()

返回RDD的元素个数

rdd.count

first()

返回RDD的第一个元素(类似于take(1))

take(n)

返回一个由数据集的前n个元素组成的数组

takeSample(withReplacement,num, [seed])

返回一个数组,该数组由从数据集中随机采样的num个元素组成,可以选择是否用随机数替换不足的部分,seed用于指定随机数生成器种子

takeOrdered(n,?[ordering])

takeOrdered和top类似,只不过以和top相反的顺序返回元素

saveAsTextFile(path)

将数据集的元素以textfile的形式保存到HDFS文件系统或者其他支持的文件系统,对于每个元素,Spark将会调用toString方法,将它装换为文件中的文本

saveAsSequenceFile(path)

将数据集中的元素以Hadoop sequencefile的格式保存到指定的目录下,可以使HDFS或者其他Hadoop支持的文件系统。

saveAsObjectFile(path)

countByKey()

针对(K,V)类型的RDD,返回一个(K,Int)的map,表示每一个key对应的元素个数。

rdd.countByKey

foreach(func)

在数据集的每一个元素上,运行函数func进行更新。

map与flatMap的区别以及mapPartiton和flatMapPartition 以及foreach和foreachPartition

map是一对一操作,输入一个元素,就输出一个元素,返回新的RDD
flatMap是输入一个元素,但是输出可以使0个或者多个元素,返回新的RDD
foreach也会遍历每一个元素,但是他是没有返回值的,不会生成RDD,而且它是一个action算子

他们都是应用于每一个元素也就是每条数据执行一次,task为每个数据,都要去执行一次function函数。

而partition算子一个task仅仅会执行一次function,function一次接收所有的partition数据。只要执行一次就可以了,性能比较高,但如果数据量过大的话会导致一个分区的数据放到内存中执行导致oom
适用于分析的数据量不是特别大的时候,或者估算一下RDD的数据量,以及每个partition的量,还有自己分配给每个executor的内存资源。看看一下子内存容纳所有的partition数据行不行

reduce与groupby的区别

reduceByKey在shuffle之前进行分区内分组聚合,在到reduce进行分区间分组聚合,减少reduce的压力

groupByKey所有的kv都会移动

spark snappy压缩率_数据集