一、RDD概述
1. 什么是RDD
RDD(Resilient Distributed Dataset)叫做分布式数据集,是Spark中最基本的数据抽象。代码中是一个抽象类,它代表一个不可变、可分区、里面的元素可并行计算的集合。
2.RDD的属性
- 一组分区(Partition),即数据集的基本组成单位;
- 一个计算每个分区的函数;
- RDD之间的依赖关系;
- 一个Partitioner,即RDD的分片函数;
- 一个列表,存储存取每个Partition的优先位置(preferred location)
3. RDD特点
RDD表示只读的分区的数据集,对RDD进行改动,只能通过RDD的转换操作,由一个RDD得到一个新的RDD,新的RDD包含了从其他RDD衍生所必需的信息。RDDs之间存在依赖,RDD的执行是按照血缘关系延时计算的。如果血缘关系较长,可以通过持久化RDD来切断血缘关系。
- 分区:
RDD逻辑上是分区的,每个分区的数据是抽象存在的,计算的时候会通过一个compute函数得到每个分区的数据。如果RDD是通过已有的文件系统构建,则compute函数是读取指定文件系统中的数据,如果RDD是通过其他RDD转换而来,则compute函数是执行转换逻辑将其他RDD的数据进行转换。 - 只读:
如下图所示,RDD是只读的,要想改变RDD中的数据,只能在现有的RDD基础上创建新的RDD。 - 由一个RDD转换到另一个RDD,可以通过丰富的操作算子实现,不再像MapReduce那样只能写map和reduce了, RDD的操作算子包括两类,一类叫做 transformations,它是用来将RDD进行转化,构建RDD的血缘关系;另一类叫做 actions,它是用来触发RDD的计算,得到RDD的相关计算结果或者将RDD保存的文件系统中。
- 依赖
RDD通过操作算子进行转换,转换得到的新RDD包含了从其他RDDs衍生所必需的信息,RDDs之间维护着这种血缘关系,也称之为依赖。如下图所示,依赖包括两种,一种是窄依赖,RDD之间分区是一对一或者多对一的,另一种是宽依赖,下游RDD的每个分区与上游RDD(也称之为父RDD)的每个分区都有关,是一对多的关系。 - 缓存
如果在应用程序中多次使用同一个RDD,可以将该RDD缓存起来,该RDD只有在第一次计算的时候会根据血缘关系得到分区的数据,在后续其他地方用到该RDD的时候,会直接从缓存处取而不用再根据血缘关系计算,这样就加速后期的重用。如下图所示,RDD-1经过一系列的转换后得到RDD-n并保存到hdfs,RDD-1在这一过程中会有个中间结果,如果将其缓存到内存,那么在随后的RDD-1转换到RDD-m这一过程中,就不会计算其之前的RDD-0了。 - CheckPoint
虽然RDD的血缘关系天然地可以实现容错,当RDD的某个分区数据失败或丢失,可以通过血缘关系重建。但是对于长时间迭代型应用来说,随着迭代的进行,RDD之间的血缘关系会越来越长,一旦在后续迭代过程中出错,则需要通过非常长的血缘关系去重建,势必影响性能。为此,RDD支持checkpoint将数据保存到持久化的存储中,这样就可以切断之前的血缘关系,因为checkpoint后的RDD不需要知道它的父RDD了, 它可以从checkpoint处拿到数据。
二、RDD编程
1.编程模型
- 在Spark中,RDD被表示为对象,通过对象上的方法调用来对RDD进行转换。经过一系列的 transformations定义RDD之后,就可以调用actions触发RDD的计算,action可以是向应用程序返回结果(count, collect等),或者是向存储系统保存数据(saveAsTextFile等)。在Spark中,只有遇到action,才会执行RDD的计算(即延迟计算),这样在运行时可以通过管道的方式传输多个转换。
- 要使用Spark,需要编写一个Driver程序,它被提交到集群以调度运行Worker,如下图所示。Driver中定义了一个或多个RDD,并调用RDD上的action,Worker则执行RDD分区计算任务。
2.RDD的创建
在Spark中创建RDD的创建方式可以分为三种:从集合中创建RDD;从外部存储创建RDD;从其他RDD创建。
import org.apache.log4j.{Level, Logger}
import org.apache.spark.rdd.RDD
import org.apache.spark.{SparkConf, SparkContext}
/**
* 生成RDD
*/
object Demo02CreatRdd {
def main(args: Array[String]): Unit = {
//控制日志输出
Logger.getLogger("org").setLevel(Level.ERROR)
/* 初始化SparkContext */
val sc = new SparkContext(new SparkConf().setAppName(this.getClass.getName).setMaster("local[2]"))
/* 创建集合 */
val array = Array(1, 2, 3, 4, 5)
/* 并行化创建RDD 默认分区2个 */
/* 1.使用集合 */
val arrayRDD1: RDD[Int] = sc.parallelize(array)
println("默认分区: " + arrayRDD1.getNumPartitions)
/* 并行化创建RDD 指定分区数 */
val arrayRDD2: RDD[Int] = sc.parallelize(array,5)
println("指定分区数: " + arrayRDD2.getNumPartitions)
//变成 kv的Rdd
val value: RDD[(Int, Int)] = arrayRDD1.map((_,1))
/* 以元组的形式创建数组 */
val arr1 = Array((1,1),(2,2),(3,3),(4,5))
//变成 kv的Rdd
val numRdd2: RDD[(Int, Int)] = sc.parallelize(arr1)
// 2.使用外部存储系统 本地 hdfs 创建RDD
val unit: RDD[String] = sc.textFile("")
}
}
3.transformations类算子
3.1.Value类型
- map(func)
作用:返回一个新的RDD,该RDD由每一个输入元素经过func函数转换后组成,每次处理一条数据。 - mapPartitions(func)
作用:类似于map,但独立地在RDD的每一个分片上运行,因此在类型为T的RDD上运行时,func的函数类型必须是Iterator[T] => Iterator[U]。假设有N个元素,有M个分区,那么map的函数的将被调用N次,而mapPartitions被调用M次,一个函数一次处理所有分区。
每次处理一个分区的数据,这个分区的数据处理完后,原RDD中分区的数据才能释放,可能导致OOM。
开发指导:当内存空间较大的时候建议使用mapPartition(),以提高处理效率。 - mapPartitionsWithIndex(func)
作用:类似于mapPartitions,但func带有一个整数参数表示分片的索引值,因此在类型为T的RDD上运行时,func的函数类型必须是(Int, Interator[T]) => Iterator[U];
import org.apache.log4j.{Level, Logger}
import org.apache.spark.rdd.RDD
import org.apache.spark.{SparkConf, SparkContext}
import scala.collection.mutable
object Demo03Transformation {
/* 测试map算子 */
def map(sc: SparkContext) = {
/* 创建数组 */
val array = Array(1, 2, 3, 4, 5, 6, 9)
/* 生成RDD */
val arrayRDD: RDD[Int] = sc.parallelize(array)
/* 用map算子聚合 将RDD中每个元素乘2 */
val mapRDD: RDD[Int] = arrayRDD.map(_ * 2)
/* 调用Action算子的 collect() 将RDD转成数组 */
val array1: Array[Int] = mapRDD.collect()
/* 转成可变的数组 */
val buffer: mutable.Buffer[Int] = array1.toBuffer
println(buffer)
}
/* 测试 mapPartition方法 */
def mapPartition(sc: SparkContext) = {
/* 创建数组 */
val array = Array(8, 1, 3, 4, 5, 6, 9)
/* 并行化生成RDD */
val arrayRDD: RDD[Int] = sc.parallelize(array)
/* 对整个分区操作 */
val mapPartitionsRDD: RDD[Int] = arrayRDD.mapPartitions(nums => {
/* 元素乘10 */
nums.map(_ * 10)
})
/* foreach遍历RDD */
mapPartitionsRDD.foreach(println(_))
}
/* 测试mapPartitionWithIndex */
def mapPartitionWithIndex(sc: SparkContext) = {
/* 创建数组 */
val array = Array(8, 1, 3, 4, 5, 6, 9)
/* 并行化生成RDD 默认两个分区 */
val arrayRDD: RDD[Int] = sc.parallelize(array)
/*
mapPartitionsWithIndex算子的匿名方法的参数
index 分区的编号
nums 整个分区的数据
*/
val mapPartitionWithIndexRDD: RDD[(Int, Int)] = arrayRDD.mapPartitionsWithIndex((index, nums) => {
nums.map(num => (index, num * 10))
})
/* foreach遍历RDD */
mapPartitionWithIndexRDD.foreach(println(_))
}
def main(args: Array[String]): Unit = {
//控制日志输出
Logger.getLogger("org").setLevel(Level.ERROR)
/* 初始化SparkContext */
val sc = new SparkContext(new SparkConf().setAppName(this.getClass.getName).setMaster("local[2]"))
/* 调用map方法 */
map(sc)
/* 调用mapPartition方法 */
mapPartition(sc)
/* 调用mapPartitionWithIndex方法 */
mapPartitionWithIndex(sc)
/* 关闭资源 */
sc.stop()
}
}
- flatMap(func)
作用:类似于map,但是每一个输入元素可以被映射为0或多个输出元素(所以func应该返回一个序列,而不是单一元素) - glom
作用:将每一个分区形成一个数组,形成新的RDD类型时RDD[Array[T]] - groupBy(func)
作用:分组,按照传入函数的返回值进行分组。将相同的key对应的值放入一个迭代器。 - filter(func)
作用:过滤。返回一个新的RDD,该RDD由经过func函数计算后返回值为true的输入元素组成。 - sample(withReplacement, fraction, seed)
作用:以指定的随机种子随机抽样出数量为fraction的数据,withReplacement表示是抽出的数据是否放回,true为有放回的抽样,false为无放回的抽样,seed用于指定随机数生成器种子。 - distinct([numTasks]))
作用:对源RDD进行去重后返回一个新的RDD。默认情况下,只有8个并行任务来操作,但是可以传入一个可选的numTasks参数改变它。 - coalesce(numPartitions)
作用:缩减分区数,用于大数据集过滤后,提高小数据集的执行效率。 coalesce重新分区,可以选择是否进行shuffle过程。由参数shuffle: Boolean = false/true决定。默认不进行shuffle。 - repartition(numPartitions)
作用:根据分区数,重新通过网络随机洗牌所有数据。repartition实际上是调用的coalesce,默认是进行shuffle的,源码如下
def repartition(numPartitions: Int)(implicit ord: Ordering[T] = null): RDD[T] = withScope {
coalesce(numPartitions, shuffle = true)
}
- sortBy(func,[ascending], [numTasks])
作用;使用func先对数据进行处理,按照处理后的数据比较结果排序,默认为正序。 - pipe(command, [envVars])
作用:管道,针对每个分区,都执行一个shell脚本,返回输出的RDD。
3.2.双Value类型交互
- union(otherDataset)
作用:并集,对源RDD和参数RDD求并集后返回一个新的RDD,不去重 - subtract (otherDataset)
作用:差集,计算差的一种函数,去除两个RDD中相同的元素,不同的RDD将保留下来 - intersection(otherDataset)
作用:交集,对源RDD和参数RDD求交集后返回一个新的RDD - cartesian(otherDataset)
作用:笛卡尔积(尽量避免使用) - zip(otherDataset)
作用:拉链,将两个RDD组合成Key/Value形式的RDD,这里默认两个RDD的partition数量以及元素数量都相同,否则会抛出异常。
3.3.Key-Value类型
- partitionBy
作用:对pairRDD进行分区操作,如果原有的partionRDD和现有的partionRDD是一致的话就不进行分区, 否则会生成ShuffleRDD,即会产生shuffle过程。 - groupByKey
作用:groupByKey也是对每个key进行操作,但只生成一个sequence。按照key进行分组,直接进行shuffle。 - reduceByKey(func, [numTasks])
在一个(K,V)的RDD上调用,返回一个(K,V)的RDD,使用指定的reduce函数,将相同key的值聚合到一起,reduce任务的个数可以通过第二个可选的参数来设置。按照key进行聚合,在shuffle之前有combine(预聚合)操作,返回结果是RDD[k,v]
开发指导:reduceByKey比groupByKey效率高,建议使用。但是需要注意是否会影响业务逻辑。 - aggregateByKey
参数:(zeroValue:U,[partitioner: Partitioner]) (seqOp: (U, V) => U,combOp: (U, U) => U) - 作用:在kv对的RDD中,,按key将value进行分组合并,合并时,将每个value和初始值作为seq函数的参数,进行计算,返回的结果作为一个新的kv对,然后再将结果按照key进行合并,最后将每个分组的value传递给combine函数进行计算(先将前两个value进行计算,将返回结果和下一个value传给combine函数,以此类推),将key与计算结果作为一个新的kv对输出。
- 参数描述:
(1)zeroValue:给每一个分区中的每一个key一个初始值;
(2)seqOp:函数用于在每一个分区中用初始值逐步迭代value;
(3)combOp:函数用于合并每个分区中的结果。 - foldByKey
参数:(zeroValue: V)(func: (V, V) => V): RDD[(K, V)]
作用:aggregateByKey的简化操作,seqop和combop相同 - combineByKey[C]
参数(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() 方法将各个分区的结果进行合并。 - sortByKey([ascending], [numTasks])
作用:在一个(K,V)的RDD上调用,K必须实现Ordered接口,返回一个按照key进行排序的(K,V)的RDD - mapValues
针对于(K,V)形式的类型只对V进行操作 - join(otherDataset, [numTasks])
作用:在类型为(K,V)和(K,W)的RDD上调用,返回一个相同key对应的所有元素对在一起的(K,(V,W))的RDD - cogroup(otherDataset, [numTasks])
作用:在类型为(K,V)和(K,W)的RDD上调用,返回一个(K,(Iterable,Iterable))类型的RDD
4.Action算子
- reduce(func)
作用:通过func函数聚集RDD中的所有元素,先聚合分区内数据,再聚合分区间数据。 - collect()
作用:在驱动程序中,以数组的形式返回数据集的所有元素。 - count()
作用:返回RDD中元素的个数 - first()
作用:返回RDD中的第一个元素 - take(n)
作用:返回一个由RDD的前n个元素组成的数组 - takeOrdered(n)
作用:返回该RDD排序后的前n个元素组成的数组 - aggregate
- 参数:(zeroValue: U)(seqOp: (U, T) ⇒ U, combOp: (U, U) ⇒ U)
- 作用:aggregate函数将每个分区里面的元素通过seqOp和初始值进行聚合,然后用combine函数将每个分区的结果和初始值(zeroValue)进行combine操作。这个函数最终返回的类型不需要和RDD中元素类型一致。
- fold(num)(func)
作用:折叠操作,aggregate的简化操作,seqop和combop一样。 - saveAsTextFile(path)
作用:将数据集的元素以textfile的形式保存到HDFS文件系统或者其他支持的文件系统,对于每个元素,Spark将会调用toString方法,将它装换为文件中的文本 - saveAsSequenceFile(path)
作用:将数据集中的元素以Hadoop sequencefile的格式保存到指定的目录下,可以使HDFS或者其他Hadoop支持的文件系统。 - countByKey()
作用:针对(K,V)类型的RDD,返回一个(K,Int)的map,表示每一个key对应的元素个数。 - foreach(func)
作用:在数据集的每一个元素上,运行函数func进行更新
import org.apache.log4j.{Level, Logger}
import org.apache.spark.rdd.RDD
import org.apache.spark.{SparkConf, SparkContext}
import scala.collection.mutable
object Demo01Transformation {
/* 测试flatMap方法 */
def flatMap(sc: SparkContext): Unit = {
/* 创建字符串数组 */
val arrStr = Array("hello hadoop", "hi hive", "ha spark")
/* 生成RDD */
val arrStrRDD: RDD[String] = sc.parallelize(arrStr)
/* 将RDD中的所有元素按空格切分 */
val value: RDD[Array[String]] = arrStrRDD.map(_.split(" "))
val array: Array[Array[String]] = value.collect()
val buffer: mutable.Buffer[Array[String]] = array.toBuffer
buffer.foreach(println(_))
/* flatMap 扁平化操作 */
val value1: RDD[String] = arrStrRDD.flatMap(_.split(" "))
value1.foreach(println(_))
}
/* 测试 glom方法 */
def glom(sc: SparkContext) = {
/* 并行化生成RDD 指定分区 4个 */
val numRDD: RDD[Int] = sc.parallelize(1 to 9, 4)
val value: RDD[Array[Int]] = numRDD.glom()
val array: Array[Array[Int]] = value.collect()
val buffer: mutable.Buffer[Array[Int]] = array.toBuffer
buffer.foreach(println(_))
value.foreach(aa => println(aa.toBuffer))
}
/* 测试 groupBy方法 */
def groupBy(sc: SparkContext) = {
/* 并行化生成RDD */
val numRDD: RDD[Int] = sc.parallelize(1 to 9)
/* groupBy 分组 按照奇偶数分组*/
val value: RDD[(Int, Iterable[Int])] = numRDD.groupBy(_ % 2)
/* 遍历 */
value.foreach(println(_))
println("-----------------------------")
val value1: RDD[(Int, Int)] = numRDD.map(num => (num % 2, num))
val value2: RDD[(Int, Iterable[Int])] = value1.groupByKey()
value2.foreach(println(_))
}
/* 测试 filter 方法 */
def filter(sc: SparkContext) = {
/* 创建字符串数组 */
val array: Array[String] = Array("hello spark", "hi hadoop", "I want study")
/* 并行化生成RDD */
val arrayRDD: RDD[String] = sc.parallelize(array)
/* 过滤掉每行数据包含 he 的数据 */
val value: RDD[String] = arrayRDD.filter(str => !str.contains("he"))
value.foreach(println(_))
println("====================")
/* 过滤掉单个包含 he 的字符 */
arrayRDD.flatMap(_.split(" ")).filter(!_.contains("he")).foreach(println(_))
}
/* 测试 sample方法 取样 */
def sample(sc: SparkContext) = {
/* 并行化生成RDD */
val numRDD: RDD[Int] = sc.parallelize(1 to 10)
/* sample方法 取样 参数: */
val value: RDD[Int] = numRDD.sample(false, 0.1)
value.foreach(println(_))
}
/* 测试 distinct方法 去重 */
def distinct(sc: SparkContext) = {
val array = Array(1, 1, 1, 2, 9, 9, 8, 4, 7, 5, 6, 2, 3, 3, 5, 1, 7, 2)
/* 并行化生成RDD */
val arrayRDD: RDD[Int] = sc.parallelize(array)
/* 指定两个分区 都去重 */
val value: RDD[Int] = arrayRDD.distinct(2)
value.foreach(println(_))
}
/* 测试 partition方法 */
def partition(sc: SparkContext) = {
/* 以元组的形式 创建数组 */
val array = Array((1, 1, 9, 9, 9, 5, 5, 6, 6, 6, 7, 7), 8, 6)
/* 并行化生成 RDD */
val arrayRDD = sc.parallelize(array)
/* 优化 增大分区 提高并行度 */
println(arrayRDD.repartition(6).getNumPartitions)
println(arrayRDD.coalesce(6).getNumPartitions)
println(arrayRDD.coalesce(6,true).getNumPartitions)
/* 减少分区 合并分区 */
println(arrayRDD.repartition(1).getNumPartitions)
println(arrayRDD.coalesce(1).getNumPartitions)
/* 优化:尽量减少shuffle类的算子 */
}
/* 测试 sortBy 方法 */
def sortBy(sc: SparkContext) = {
val array = Array(5, 3, 4, 1, 2)
/* 并行化生成RDD */
val arrayRDD: RDD[Int] = sc.parallelize(array)
/* 升序 指定分区个数为1 */
val value: RDD[Int] = arrayRDD.sortBy(num => num, true, 1)
value.foreach(println(_))
}
/* 测试 uion 方法 */
def uion(sc: SparkContext) = {
/* 并行化生成RDD */
val uionRDD1: RDD[Int] = sc.parallelize(1 to 2)
val uionRDD2: RDD[Int] = sc.parallelize(2 to 3)
/* 合并两个RDD */
val value: RDD[Int] = uionRDD1.union(uionRDD2)
value.foreach(println(_))
}
/* 测试 subtract 方法 交集 差集 笛卡尔积 拉链 */
def subtract(sc: SparkContext) = {
/* 以元组的形式 创建数组 */
val array1 = Array((1,2,3,4,5),3,5)
val array2 = Array((3,4,5,6,7),5,6)
/* 并行化生成RDD */
val array1RDD = sc.parallelize(array1, 3)
val array2RDD = sc.parallelize(array2,3)
/* 差集 */
val value1 = array1RDD.subtract(array2RDD)
value1.foreach(println(_))
println("----------------")
/* 交集 */
array1RDD.intersection(array2RDD).foreach(println(_))
println("----------------")
/* 笛卡尔积 */
array1RDD.cartesian(array2RDD).foreach(println(_))
println("----------------")
/* zip 拉链 */
array1RDD.zip(array2RDD).foreach(println(_))
}
/* 测试 partitionBy 方法 */
def partitionBy(sc: SparkContext) = {
/* 以元组的形式 创建数组 */
val array1 = Array((1,2,3,4,5),3,5)
/* 并行化生成RDD */
val array1RDD: RDD[Any] = sc.parallelize(array1)
val value: RDD[(Any, Any)] = array1RDD.map(num => (num, num))
value.foreach(println(_))
}
def groupByKey(sc: SparkContext) = {
val words = Array("one", "two", "two", "three", "three", "three")
val wordsRdd = sc.parallelize(words)
// wordsRdd.count()
wordsRdd.map((_,1))
.groupByKey(1) //按照key 分组 指定1个分区
.foreach(println(_))
}
def reduceByKey(sc: SparkContext) = {
val numRdd01 = sc.parallelize(Array("one", "three", "two", "two", "three", "three"))
val pairRdd = numRdd01.map(word => (word, 1))
pairRdd.cache() //持久化
pairRdd.reduceByKey(_+_)
// .reduceByKey((a:Int,b:Int) => a+b)
.foreach(println(_))
println("---------")
//先分区内合并,然后做最后的合并 100 是每个分区的初始值
pairRdd.aggregateByKey(100)(_+_,_+_) .foreach(println(_))
println("---------")
pairRdd.combineByKey(init => init+100,(a:Int,b:Int)=>a+b,(a:Int,b:Int)=>a+b) .foreach(println(_))
}
def foldBykey(sc: SparkContext) = {
val numRdd = sc.parallelize(List((1,3),(1,2),(1,4),(2,3),(3,6),(3,8)),3)
numRdd.foldByKey(100)(_+_).foreach(println(_))
}
def sortByKey(sc: SparkContext) = {
val rdd = sc.parallelize(Array((3,"aa"),(6,"cc"),(2,"bb"),(1,"dd")))
//降序 1个分区
rdd.sortByKey(false,1)
//取前2名
.take(2)
.foreach(println(_))
println("-------")
rdd.sortBy(_._1,false,1).foreach(println(_))
println("-------")
//对kv的Rdd 做value 操作
rdd.mapValues(_*2).foreach(println(_))
}
def join(sc: SparkContext) = {
val rdd01 = sc.parallelize( Array(("A",1),("B",2),("C",3)) )
val rdd02 = sc.parallelize( Array(("A",true),("B",false)) )
val j1Rdd: RDD[(String, (Int, Boolean))] = rdd01.join(rdd02)
j1Rdd.foreach(println(_))
println("---------")
val v1: RDD[(String, (Int, Option[Boolean]))] = rdd01.leftOuterJoin(rdd02)
v1.foreach(println(_))
println("-------")
v1.foreach(tuple=>{
val k = tuple._1
val v1 = tuple._2._1
val value: Option[Boolean] = tuple._2._2
println((k,v1,value.getOrElse(0)))
})
//cogroup
val v2: RDD[(String, (Iterable[Int], Iterable[Boolean]))] = rdd01.cogroup(rdd02)
v2.foreach(println(_))
}
def main(args: Array[String]): Unit = {
//控制日志输出
Logger.getLogger("org").setLevel(Level.ERROR)
/* 初始化SparkContext */
val sc = new SparkContext(new SparkConf().setAppName(this.getClass.getName).setMaster("local[2]"))
/* 调用flatMap方法 */
//flatMap(sc)
/* 调用glom方法 */
//glom(sc)
/* 调用groupBy方法 */
//groupBy(sc)
/* 调用filter方法 */
//filter(sc)
/* 调用 sample 取样 方法 */
//sample(sc)
/* 调用 distinct 去重 方法 */
//distinct(sc)
/* 调用 partition 方法 */
//partition(sc)
/* 调用 sortBy 排序 方法 */
//sortBy(sc)
/* 调用 uion 方法 */
//uion(sc)
/* 调用 subtract 方法 差集 交集 笛卡尔积 拉链 */
//subtract(sc)
/* 调用 partitionBy 方法 */
//partitionBy(sc)
// groupByKey(sc)
// reduceByKey(sc)
// foldBykey(sc)
// sortByKey(sc)
join(sc)
/* 关闭资源 */
sc.stop()
}
}