Spark RDD的函数详解

RDD支持两种操作:转换(transformation)从现有的数据集创建一个新的数据集;而动作(actions)在数据集上运行计算后,返回一个值给驱动程序。 区别是tranformation输入RDD,输出RDD,而action输入RDD,输出非RDD。transformation是缓释执行的,action是即刻执行的。例如,df1.map就是一种转换,它在使用时,并没有被调用,只有和df1相关的action发生时,df1才会被加载到内存,及时前面df1被加载过,若没有persist或cache,也需要重新加载。reduce是一种action,通过一些函数将所有的元素叠加起来,并将最终结果返回给Driver程序。(不过还有一个并行的reduceByKey,能返回一个分布式数据集)


Spark中的所有转换都是惰性的,也就是说,他们并不会直接计算结果。相反的,它们只是记住应用到基础数据集(例如一个文件)上的这些转换动作。只有当发生一个要求返回结果给Driver的动作时,这些转换才会真正运行。这个设计让Spark更加有效率的运行。例如,我们可以实现:通过map创建的一个新数据集,并在reduce中使用,最终只返回reduce的结果给driver,而不是整个大的新数据集。

默认情况下,每一个转换过的RDD都会在你在它之上执行一个动作时被重新计算。不过,你也可以使用persist(或者cache)方法,持久化一个RDD在内存中。在这种情况下,Spark将会在集群中,保存相关元素,下次你查询这个RDD时,它将能更快速访问。在磁盘上持久化数据集,或在集群间复制数据集也是支持的。

下表列出了Spark中的RDD转换和动作(Spark 1.5.1)。每个操作都给出了标识,其中方括号表示类型参数。前面说过转换是延迟操作,用于定义新的RDD;而动作启动计算操作,并向用户程序返回值或向外部存储写数据。

转换(Transformation):

 函数名 描述
map[U: ClassTag](f: T => U): RDD[U] 将变换函数f应用于RDD的每个元素,并将结果返回构成新的RDD
 flatMap[U: ClassTag](f: T => TraversableOnce[U]): RDD[U] 将变换函数f应用于RDD的每个元素,并将结果fatten后返回构成新的RDD
 filter(f: T => Boolean): RDD[T] 返回由断言为true的元素构成的新RDD
 distinct(numPartitions: Int)(implicit ord: Ordering[T] = null): RDD[T] 返还RDD中的不同元素构成新RDD
repartition(numPartitions: Int)(implicit ord: Ordering[T] = null): RDD[T] 返还拥有numPartitions个分区的RDD,数据先被shuffle再分配
 coalesce(numPartitions: Int, shuffle: Boolean = false, partitionCoalescer: Option[PartitionCoalescer] = Option.empty) (implicit ord: Ordering[T] = null): RDD[T] shuffle在false情况下只能返还减少分区到numPartition的RDD。在true情况下可向上增大分区到numPartition
 sample(withReplacement: Boolean,fraction: Double, seed: Long = Utils.random.nextLong): RDD[T] 返还当前RDD子集元素的RDD。withReplacement:true 选取元素可重复,fraction决定每个元素重复次数(>=0);withReplacement:false 选取元素不可重复,fraction决定每个元素被选取的概率([0,1])
 randomSplit(weights: Array[Double], seed: Long = Utils.random.nextLong): Array[RDD[T]] RDD被按比例数组随机分成RDD数组返回,比例数组和不为1的话会被均一化,注意各部分RDD数据是根据seed随机抽取的
 takeSample(withReplacement: Boolean,num: Int, seed: Long = Utils.random.nextLong): Array[T] 返回包含num个随机元素的数组,返回数组储存在driver内存,慎用
 union(other: RDD[T]): RDD[T] 返回RDD1和RDD2的合集,相同元素重复出现
 ++(other: RDD[T]): RDD[T] 同上
 sortBy[K](f: (T) => K,ascending: Boolean = true, numPartitions: Int = this.partitions.length) (implicit ord: Ordering[K], ctag: ClassTag[K]): RDD[T] 根据函数f排序,返回排序后的RDD
 intersection(other: RDD[T]): RDD[T] 返回RDD1和RDD1的交集,无重复元素,即使RDD1含有重复元素,并且交集被shuffle后返回
intersection(other: RDD[T],partitioner: Partitioner) (implicit ord: Ordering[T] = null): RDD[T] 同上,将使用分区器
 intersection(other: RDD[T], numPartitions: Int): RDD[T] 同上上,使用hash分区将返回RDD分为numPartittion个区
 glom(): RDD[Array[T]] RDD中每一个分区中类型为T的元素转换成Array[T],这样每一个分区就只有一个数组元素
 cartesian[U: ClassTag](other: RDD[U]): RDD[(T, U)] 该函数返回的是Pair类型的RDD,计算结果是当前RDD和other RDD中每个元素进行笛卡儿计算的结果
 groupBy[K](f: T => K)(implicit kt: ClassTag[K]): RDD[(K, Iterable[T])] 返回分组RDD,每个组包含一个key,以及一列映射到这个key的元素,每次调用每组的元素顺序是无法保证的。注意:此方法非常耗费资源,若仅是为了配合agg函数(如cont,sum等),推荐使用更高效的`PairRDDFunctions.aggregateByKey`或 `PairRDDFunctions.reduceByKey`
 groupBy[K](f: T => K,numPartitions: Int)(implicit kt: ClassTag[K]): RDD[(K, Iterable[T])] 同上,多了分区数设置
 groupBy[K](f: T => K, p: Partitioner)(implicit kt: ClassTag[K], ord: Ordering[K] 同上,多了分区设置
 pipe(command: String): RDD[String] 准备好外部程序A,rdd.pipe(A) 将会把rdd中每个元素做为A的输入,然后输出组成一个新的RDD。有点像map
 pipe(command: String, env: Map[String, String]): RDD[String] 同上
 pipe(command: Seq[String],env: Map[String, String] = Map(), printPipeContext: (String => Unit) => Unit = null, printRDDElement: (T, String => Unit) => Unit = null, separateWorkingDir: Boolean = false,bufferSize: Int = 8192, encoding: String = Codec.defaultCharsetCodec.name): RDD[String] 超复杂,暂略
 mapPartitions[U: ClassTag](f: Iterator[T] => Iterator[U], preservesPartitioning: Boolean = false): RDD[U] 按partition抽取数据并通过函数f,详见该函数与map函数的区别,这里略。pre..Par..g参数只有在pair RDD时 且输入函数不改变键值时才为true
 mapPartitionsWithIndex[U: ClassTag]( f: (Int, Iterator[T]) => Iterator[U], preservesPartitioning : Boolean = false): RDD[U] 同上,同时保持对原partition index的跟踪
 zip[U: ClassTag](other: RDD[U]): RDD[(T, U)]按元素zip两个RDD形成key-value RDD, 两RDD需要有相同数量的分区且每分区有相同数量的元素(最方便的是一个rdd由另一个map而来)
 zipPartitions[B: ClassTag, V: ClassTag] (rdd2: RDD[B], preservesPartitioning: Boolean) (f: (Iterator[T], Iterator[B]) => Iterator[V]): RDD[V] 按分区zip rdd, 要求两rdd有相同的分区数,不要求每分区数有相同元素,同时将zip后的rdd应用于函数f,得到新的RDD。
  zipPartitions[B: ClassTag, V: ClassTag] (rdd2: RDD[B], (f: (Iterator[T], Iterator[B]) => Iterator[V]): RDD[V] 同上,没有是否保留父rdd信息参数
  def zipPartitions[B: ClassTag, C: ClassTag, D: ClassTag, V: ClassTag] (rdd2: RDD[B], rdd3: RDD[C], rdd4: RDD[D]) (f: (Iterator[T], Iterator[B], Iterator[C], Iterator[D]) => Iterator[V]): RDD[V] 同上,多了几个rdd参数,这是能支持的最多情况,也支持保留父rdd选项形式,这里省略


 

动作(Actions):

A&C函数: 满足结合律和交换律的函数

Associative :a + (b + c) = (a + b) + c , f(a,f(b,c))=f(f(a,b),c) if f(a,b)=a+b

Commutative:ab=ba ,f(a,b)=f(b,a) if f(a,b)=a*b

  foreach(f: T => Unit): Unit 将无返回函数应用于rdd每个元素
  foreachPartition(f: Iterator[T] => Unit): Unit 将无返回函数应用于rdd每个分区
  collect(): Array[T] 返回包含rdd所有元素的数组,返回到driver端,注意内存管理
  toLocalIterator: Iterator[T] 返回包含所有元素的iterator,占用内存为rdd最大分区内存 注意:使用此动作前rdd最好先persist
 collect[U: ClassTag](f: PartialFunction[T, U]): RDD[U] 返回所有匹配函数f的元素组成的rdd
 subtract(other: RDD[T]): RDD[T] 返回在调用rdd中但不在other rdd中的元素组成的rdd,采用调用rdd的分区
 subtract(other: RDD[T], numPartitions: Int): RDD[T] 同上,支持重新分区
 subtract( other: RDD[T],p: Partitioner)(implicit ord: Ordering[T] = null): RDD[T] 同上,支持分区和排序
 reduce(f: (T, T) => T): T 根据A&C映射函数f,对RDD中的元素进行二元计算,返回计算结果,若函数不是A&C的,结果不可测
 treeReduce(f: (T, T) => T, depth: Int = 2): T 以多层tree模式进行reduce, 可用来减少reduce开销,f需是A&C函数
 fold(zeroValue: T)(op: (T, T) => T): T 在每个分区内做聚合,再将分区内结果做聚合,聚合顺序是不固定的,需要f为A&C的
 aggregate[U: ClassTag](zeroValue: U)(seqOp: (U, T) => U, combOp: (U, U) => U): U 在每个分区内做聚合,再将分区内结果做聚合。可变换类型 即rdd[T]=>U,seqOp用于分区内运算,可做类型变换,combOp用于分区结果合并,需满足结合律A
 treeAggregate[U: ClassTag](zeroValue: U)( seqOp: (U, T) => U, combOp: (U, U) => U, depth: Int = 2): U 同上,用了多层树模型,更高效
 count(): Long 返回rdd的元素数
 countApprox(timeout: Long,confidence: Double = 0.95) : PartialResult[BoundedDouble] 在最长等待时间timeout 毫秒内返回置信度为confidence的近似结果
 countByValue()(implicit ord: Ordering[T] = null): Map[T, Long] 计算每个元素出现的次数,返回Map到driver端。对于很大的rdd建议用rdd.map(x => (x, 1L)).reduceByKey(_ + _) 得到RDD[T,Long]而不是本地Map
 countByValueApprox(timeout: Long, confidence: Double = 0.95) (implicit ord: Ordering[T] = null) : PartialResult[Map[T, BoundedDouble]] 上面的近似值
 countApproxDistinct(p: Int, sp: Int): Long 返回rdd中不同元素数的近似值
 countApproxDistinct(relativeSD: Double = 0.05): Long 同上,提供准确度设置值,越小越准,花费空间越大,必须大于0.000017
 zipWithIndex(): RDD[(T, Long)] zip rdd的元素和相应元素编号,编号顺序先按分区再按分区内元素顺序。第一个分区第一个元素编号为0, 最后一分区最后一元素编号最大。如需顺序,需要用sortByKey保证
 zipWithUniqueId(): RDD[(T, Long)] zip rdd的元素和相应的独立id, 独立id比起编号来说是有间隙的,如需顺序,需要用sortByKey保证
 take(num: Int): Array[T] 返回rdd的前num个元素组成array到driver。若rdd为nothing或null会报错
 first(): T 返回rdd的第一个元素
 top(num: Int)(implicit ord: Ordering[T]): Array[T] 返回排序后的前num个元素组成的数组到driver端,默认降序排列,sc.parallelize(Seq(2, 3, 4, 5, 6)).top(2) 返回Array(6, 5)
 takeOrdered(num: Int)(implicit ord: Ordering[T]): Array[T] 同上,顺序相反,sc.parallelize(Seq(2, 3, 4, 5, 6)).takeOrdered(2)返回Array(2, 3)
 max()(implicit ord: Ordering[T]): T 按指定顺序返回rdd的max元素
 min()(implicit ord: Ordering[T]): T  按指定顺序返回rdd的min元素
 isEmpty(): Boolean 判断rdd是否为空(空分区或空元素都为空,即使分区有一个,元素为空也为空)。注意:为Nothing或null的RDD引用会抛出异常。 `parallelize(Seq())` 为 `RDD[Nothing]`, (`parallelize(Seq())` 可通过 `parallelize(Seq[T]())`.)避免
 saveAsTextFile(path: String): Unit 将RDD储存为text文件,元素用其toString方式表示,所以注意不要有类似Array这类toString后丧失信息的元素。此函数兼容null值
 saveAsTextFile(path: String, codec: Class[_ <: CompressionCodec]): Unit 同上,利用压缩形式储存
 saveAsObjectFile(path: String): Unit 储存为二进制序列化文件,对象为序列化对象
 keyBy[K](f: T => K): RDD[(K, T)] 通过f生成key-value rdd
 private[spark] def collectPartitions(): Array[Array[T]] 私有测试方法,查看每个分区内容
 checkpoint(): Unit = RDDCheckpointData.synchronized 储存到指定SparkContext#setCheckpointDir位置,使用后会清除所有lineage信息,作业完成后也不会清除数据。建议使用前先persist,否则会重复计算
 localCheckpoint(): this.type = RDDCheckpointData.synchronized 用于缩减lineage信息,由于使用了本地临时存储,而不是容灾存储,所以不
 isCheckpointed: Boolean 判断rdd是否checkpointed和materialized
private[rdd] def isLocallyCheckpointed: Boolean 私有方法,判断是否localCheckpointed
 getCheckpointFile: Option[String] 获取rdd checkpointed的路径名称

 

还有些其他内部方法和域,省略。

 

特例:

sc.parallelize(Array(2., 3.)).fold(0.0)((p, v) => p+v*v)

由于(p, v) => p+v*v不满足交换律,所以结果不可知,可改为:

 

sc.parallelize(Array(2., 3.)).map(v=>v*v).reduce(_+_)

 

其他:

class BoundedDouble(val mean: Double, val confidence: Double, val low: Double, val high: Double) {
  override def toString(): String = "[%.3f, %.3f]".format(low, high)
}

 

 

 

persist和checkpoint区别:

详情参考:https://github.com/JerryLead/SparkInternals/blob/master/markdown/english/6-CacheAndCheckpoint.md

https://stackoverflow.com/questions/35127720/what-is-the-difference-between-spark-checkpoint-and-persist-to-a-disk

 

Persist

  • Persisting or caching with StorageLevel.DISK_ONLY cause the generation of RDD to be computed and stored in a location such that subsequent use of that RDD will not go beyond that points in recomputing the linage.
  • After persist is called, Spark still remembers the lineage of the RDD even though it doesn't call it.
  • Secondly, after the application terminates, the cache is cleared or file destroyed

Checkpointing

  • Checkpointing stores the rdd physically to hdfs and destroys the lineage that created it.
  • The checkpoint file won't be deleted even after the Spark application terminated.
  • Checkpoint files can be used in subsequent job run or driver program
  • Checkpointing an RDD causes double computation because the operation will first call a cache before doing the actual job of computing and writing to the checkpoint directory.

 

PairRDD 函数


https://github.com/apache/spark/blob/master/core/src/main/scala/org/apache/spark/rdd/PairRDDFunctions.scala

 

 

参考资料

https://github.com/apache/spark/blob/master/core/src/main/scala/org/apache/spark/rdd/RDD.scala

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值