分类
Transformation(转换):
概念
将一个RDD通过一系列操作变为另一个RDD的过程,这个操作可能是简单的加减操作,也可能是某个函数或某一系列函数。
注:
- 所有Transformation函数都是Lazy(惰性的),不会立即执行,需要Action函数来触发
- Transformation操作不会触发真正的计算,只会建立RDD的关系图
Action(动作)
概念
Action操作代表依次计算的结束,返回值不是RDD,将结果返回到Driver程序或输出到外部(文件或文件夹)。
注:
- 所有Action函数立即执行(Eager),比如reduce、saveAsTextFile、count等。
- 所以Transformation只是建立计算关系,Action才是实际的执行者。
- 每个Action操作都会形成一个DAG调用SparkCoutext的runJob方法向集群提交请求,所以每个Action操作都对应一个DAG/Job。
Spark中的重要函数
基本函数
概述
RDD中map、filter、flatMap及foreach等函数为最基本函数,都是都RDD中每个元素进行操作,将元素传递到函数中进行转换。
- map函数:
- map(f:T=>U) : RDD[T]=>RDD[U],表示将 RDD 经由某一函数 f 后,转变为另一个RDD。
- flatMap函数:
- flatMap(f:T=>Seq[U]) : RDD[T]=>RDD[U]),表示将 RDD 经由某一函数 f 后,转变为一
个新的 RDD,但是与 map 不同,RDD 中的每一个元素会被映射成新的 0 到多个元素
(f 函数返回的是一个序列 Seq)。
- filter函数:
- filter(f:T=>Bool) : RDD[T]=>RDD[T],表示将 RDD 经由某一函数 f 后,只保留 f 返回
为 true 的数据,组成新的 RDD。
- foreach 函数:
- foreach(func),将函数 func 应用在数据集的每一个元素上,通常用于更新一个累加器,
或者和外部存储系统进行交互,例如 Redis。关于 foreach,在后续章节中还会使用,到
时会详细介绍它的使用方法及注意事项。
- saveAsTextFile函数:
- saveAsTextFile(path:String),数据集内部的元素会调用其 toString 方法,转换为字符串
形式,然后根据传入的路径保存成文本文件,既可以是本地文件系统,也可以是HDFS 等。
分区操作函数
概述
每个RDD由多分区组成的,实际开发建议对每个分区数据的进行操作,map函数使用
mapPartitions代替、foreache函数使用foreachPartition代替。
案例(重写wordCount)
使用分区操作函数重写wordCount:
import org.apache.spark.{SparkConf, SparkContext, TaskContext}
import org.apache.spark.rdd.RDD
object RDD_分区操作 {
def main(args: Array[String]): Unit = {
//TODO 1.env/准备sc/SparkContext/Spark上下文执行环境
val conf: SparkConf = new SparkConf().setAppName("wc").setMaster("local[*]");
val sc: SparkContext = new SparkContext(conf)
sc.setLogLevel("WARN")
//TODO 2.source/读取数据
//RDD:A Resilient Distributed Dataset (RDD):弹性分布式数据集,简单理解为分布式集合!使用起来和普通集合一样简单!
//RDD[就是一行行的数据]
val lines: RDD[String] = sc.textFile("input/words.txt")
//获取分区数
println(lines.getNumPartitions)
//将一行行的数据切割成一个个单词,针对每个分区进行操作,使用迭代器迭代每个分区的数据,并进行分组聚合
val result: RDD[(String, Int)] = lines.flatMap(_.split(" ")).mapPartitions(iter => {
iter.map((_,1))
}).reduceByKey(_+_)
//遍历每个分区
result.foreachPartition(iter =>{
//获取分区编号
val partitionId: Int = TaskContext.getPartitionId()
println(s"第${partitionId}个分区:")
iter.foreach(print(_))
println()
})
}
}
重分区函数
概念
对RDD中分区的数据进行调整的函数(增加或减少函数)
增加/减少分区函数
-
函数名称:repartition,此函数使用的谨慎,会产生Shuffle
def repartition(numPartitions:Int)(implicit ord:Ordering[T] = null):RDD[T]
-
函数名称:coalesce,此函数在减少RDD分区数目时不会产生Shuffle
def coalesce(
numPartitions:Int,
shuffle:Boolean = false,
partitionCoalescer:Option[PartitionCoalescer] = Option.empty
)(implicit ord:Ordering[T] = null):RDD[T]
- 函数名称:partitionBy(partitioner:Partitioner):RDD[(K,V)]
在PairRDDFunctions(此类专门针对RDD中数据类型为KeyValue对提供函数)工具类中
partitionBy函数:
//此函数通过传递分区器Partitioner改变RDD的分区数据
def patitionBy(partitioner:Partitionner):RDD[(K,V)]
注:
1. repartition的底层就是开启了Shuffle的coalesce
2. 当coalesce在参数列表中指定了Shuffle为true,也可以实现对分区数量的添加
3. coalesce在默认情况下不能实现对分区数量的增加
聚合函数
概述
在数据分析领域中,对数据聚合操作是最为关键的,在Spark框架中各个模块使用时,主要就 是其中聚合函数的使用。
分类
- 集合中的聚合函数
- 函数:reduce
def reduce[A1 > A](op:(A1,A1):A1
- 函数:fold
def fold[A1 > A](z:A1)(op:(A1,A1) => A1):A1
- RDD中的聚合函数:同列表中的聚合函数reduce和fold
- PairRDDFunctions聚合函数
概述:
在Spark中有一个object对象PairRDDFunctions,主要针对RDD的数据类型是Key/Value对的数
据提供函数,方便数据分析处理。比如使用过的函数:reduceByKey、groupByKey等。*ByKey函 数:将相同Key的Value进行聚合操作的,省去先分组再聚合。
- 分组函数groupByKey
将相同Key的Value合在一起,所有的Value存储在迭代器Iterable中
def groupByKey():RDD[(K,Iterable[V])]
注:
此函数容易出现性能问题:
- 数据倾斜
当某个Key对应的Value值非常多的时候,迭代器中的数据Value非常多- OOM:内存溢出
在Spark开发中,原则:能不适用groupByKey就不要适用,不得已而为之,可以完全不用适用
- 分组聚合函数reduceByKey和foladByKey
// 将相同的Key的Value进行聚合操作,类似RDD中的reduce函数
def reduceBykey(func:(V,V) => V):RDD[(K,V)]
//此函数比reduceByKey多了对聚合时中间临时变量初始值,类似RDD中的fold函数
def foldBykey(zeroValue:V)(func :(V,V) => V):RDD[(K,V)]
注:
- 在SparkCore开发中,建议使用reduceByKey函数(它不存在分组,不会出现由于数据量过大引起的数据倾斜和内存溢出问题)
- reduceByKey和foldByKey聚合以后的结果类型与RDD中Value的数据类型是一样的。
- 分组聚合函数aggregateByKey
概述:它与reduceByKey类似,但它更加灵活,可以自定义分区内和分区之间的操作,所以在企业中如果对数据聚合使用,不能使用reduceByKey完成时,考虑使用aggregateByKey函数
def aggregate[U:ClassTag]
(zeroValue:U)(
seqOp:(U,V) => U,
combOp:(U,U) = U
):RDD[(K,U)]
关联函数
概述
当两个RDD的数据类型为二元组Key/Value对时,可以依据Key进行关联Join。
1. 在SQL中JOIN时:
指定关联字字段 a join b on a.xxx=b.yyy
2. 在RDD数据JOIN时,要求数据类型必须是二元组(Key,Value)
根据Key进行关联的
SQL JOIN的Venn图:
RDD中关联JOIN函数都在PairRDDFunctions中,具体截图如下:
等值JOIN函数:
def join[W](other:RDD[(K,W)]):RDD[K,(V,W)]
def lefrOuterJoin[W](other:RDD[(K,W)]):RDD[(K,(V,Option[W]))]
def rightOuterJoin[W](other:RDD[(K,W)]):RDD[(K,(V,Option[W]))]