概述
本文介绍RDD的Transformations函数的原理和作用。还会介绍transformations函数的分类,和不同类型的转换产生的效果。
RDD的Transformations操作
transformations简介
在RDD中定义了两类函数:action和transformations。transformations通过在一些RDD中执行一些数据操作来产生一个或更多新的RDD。这些transformations函数包括:map,filter,join,reduceByKey,cogroup,randomSplit等。
也就是说,transformations是这样的一系列函数,它们的输入是一个RDD,输出是一个或多个RDD。但这些函数并不会改变输入RDD的值(这是RDD不可改变的特性),但通过transformations函数的计算会产生一个或多个新的RDD。
transformations的性质
RDD的transformations函数是懒评估(evaluated lazily)的。所谓懒评估是指:在调用transformations函数时不会立即执行,直到action函数被调用。也就是说,transformations函数的执行是由action函数的调用来触发的。
通过使用transformations转换函数,您可以使用最终RDD的所有父RDD逐步构建RDD血缘(RDD Lineage)。
一个RDD通过transformations转换函数处理后得到的新的结果RDD通常与父RDD的值不同,该结果RDD的数据集可能变得更大(例如:flatMap,union等),也可能变得更小(例如:filter,distinct,count等),或则大小相同(例如:map等)。
注意:有些转换函数也可能发起计算,例如:例如sortBy,zipWithIndex等
transformations函数的使用
安装好spark(可以是单机的),并启动spark-shell,在spark-shell中输入一下命令:
1. 加载一个文件,文件内容不重要
scala> val file = sc.textFile("derby.log")
file: org.apache.spark.rdd.RDD[String] = derby.log MapPartitionsRDD[1] at textFile at <console>:24
2. 查看file的结果
scala> file.toDebugString
res14: String =
(2) derby.log MapPartitionsRDD[1] at textFile at <console>:24 []
| derby.log HadoopRDD[0] at textFile at <console>:24 []
3. 把文件中的内容按\s+进行分割,也可以执行其他文本操作
scala> val allWords = file.flatMap(_.split("\\s+"))
allWords: org.apache.spark.rdd.RDD[String] = MapPartitionsRDD[2] at flatMap at <console>:26
4. 查看一下allWords这个RDD的linage或逻辑执行计划
scala> allWords.toDebugString
res0: String =
(2) MapPartitionsRDD[2] at flatMap at <console>:26 []
| derby.log MapPartitionsRDD[1] at textFile at <console>:24 []
| derby.log HadoopRDD[0] at textFile at <console>:24 []
从以上第2步的代码和输出可以看到,执行sc.textFile函数后,会产生两种RDD,一种是:HadoopRDD,一种是:MapPartitionsRDD,其中HadoopRDD是中间状态的RDD,最后得到的是MapPartitionsRDD。这个结果从第1步的系统输出可以看到。
然后再对文件中的内容进行处理,会产生新的RDD,产生的新的RDD也是MapPartitionsRDD类型的。从第4步的输出可以看到。
为了更好的理解这些输出,我们可以看一下这些函数的源代码:
def textFile(
path: String,
minPartitions: Int = defaultMinPartitions): RDD[String] = withScope {
assertNotStopped()
hadoopFile(path, classOf[TextInputFormat], classOf[LongWritable], classOf[Text],
minPartitions).map(pair => pair._2.toString).setName(path)
}
可以看到textFile会调用hadoopFile函数创建一个HadoopRDD,然后再执行map操作,这样就得到了一个MapPartitionsRDD,然后再对该RDD设置一个命令,该RDD的名称被设置为参数path的值。
scala> file.name
res18: String = derby.log
窄转换(Narrow transformations)和宽转换(Wide transformations)
窄转换(Narrow transformations)
窄转换是基于窄依赖(narrow dependencies)进行的RDD转换。
所谓窄依赖是指:父RDD的每个分区最多被儿子RDD的一个分区使用。
因为窄依赖,所以输出的结果RDD的分区数据,都是基于父RDD的单个分区得到的,计算结果是父RDD单个分区数据的子集,所以窄转换不会产生shuffle。
产生窄转换的函数有:map,filter,distinct等。
Spark可以将窄转换进行分组,形成一个pipeline,以便提高计算效率。
宽转换(wide transformations)
宽转换是基于宽依赖(wide dependencies)进行的RDD转换。
所谓宽依赖是指:父RDD的每个分区都可能被子RDD的多个分区使用。
也就是说,计算单个分区中的记录所需的数据可能存在父RDD的多个分区中。所以,宽转换很可能会发生shuffle过程,有时候把宽转换也称为:shuffle transformations。
具有相同key的所有元组必须最终位于同一分区中,由同一任务处理。为了满足这些操作,Spark必须执行RDD shuffle动作,它在集群之间传输数据,使用一组新的分区创建一个新阶段。
导致宽转换的函数有:groupByKey,reduceByKey等。
总结
本文分析了spark RDD的Transformations性质和原理,关于transformations函数的使用可以