RDD编程

在Spark中,RDD被表示为对象,通过对象上的方法调用来对RDD进行转换。经过一系列的transformations定义RDD之后,就可以调用actions触发RDD的计算,action可以是向应用程序返回结果(count, collect等),或者是向存储系统保存数据(saveAsTextFile等)。在Spark中,只有遇到action,才会执行RDD的计算(即延迟计算),这样在运行时可以通过管道的方式传输多个转换。

RDD的创建:
  • 集合中创建
  1. parallelize()
    val rdd = sc.parallelize(Array(1,2,3,4,5))
  2. makeRDD()
    val rdd = sc.makeRDD(Array(1,2,3,4,5))
RDD的转换(重点)

RDD整体上分为Value类型和Key-Value类型

  • Value类型:
  • map(func):返回一个新的RDD,该RDD由每一个输入元素经过func函数转换后组成
    示例:创建一个1-10数组的RDD,将所有元素*2形成新的RDD
    val res = sc.makeRDD(1 to 10)res.collect() //Array[Int] = Array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)val maprdd = rws.map( _ * 2)maprdd.collect() // Array[Int] = Array(2, 4, 6, 8, 10, 12, 14, 16, 18, 20)
  • mapPartitions(func)
//作用:类似于map,但独立地在RDD的每一个分片上运行,
//因此在类型为T的RDD上运行时,func的函数类型必须是Iterator[T] => Iterator[U]。
//假设有N个元素,有M个分区,那么map的函数的将被调用N次,
//而mapPartitions被调用M次,一个函数一次处理所有分区;

//需求:创建一个RDD,使每个元素*2组成新的RDD:
val rdd = sc.parallelize(Array(1,2,3,4))
val rdd2=rdd.mapPartitions(x=>x.map(_*2))
rdd2.collect()
//Array[Int] = Array(2, 4, 6, 8)
  • mapPartitionsWithIndex(func)
//作用:类似于mapPartitions,但func带有一个整数参数表示分片的索引值,
//因此在类型为T的RDD上运行时,func的函数类型必须是(Int, Interator[T]) => Iterator[U]

//需求:创建一个RDD,使每个元素跟所在分区形成一个元组组成一个新的RDD
val rdd = sc.parallelize(Array(1,2,3,4))
val indexRdd = rdd.mapPartitionsWithIndex((index,items)=>(items.map((index,_))))
indexRdd.collect() 
//Array[(Int, Int)] = Array((0,1), (0,2), (1,3), (1,4))
  • flatMap(func)
//作用:类似于map,但是每一个输入元素可以被映射为0或多个输出元素
//(所以func应该返回一个序列,而不是单一元素)

//示例:
val sourceFlat = sc.parallelize(1 to 5)
val flatMap = sourceFlat.flatMap(1 to _)//(1->1,2->1,2……5->1,2,3,4,5)
flatMap.collect()
//Array[Int] = Array(1, 1, 2, 1, 2, 3, 1, 2, 3, 4, 1, 2, 3, 4, 5)
  • map()和mapPartition()的区别:
    map():每次处理一条数据;
    mapPartition():每次处理一个分区的数据,这个分区的数据处理完后,原RDD中分区的数据才能释放,可能导致OOM;
  • glom
//作用:将每一个分区形成一个数组,形成新的RDD类型时RDD[Array[T]]

//案例:创建一个4个分区的RDD,并将每个分区的数据放到一个数组
val rdd = sc.parallelize(1 to 16,4)
rdd.glom().collect()
// Array[Array[Int]] = Array(Array(1, 2, 3, 4), Array(5, 6, 7, 8), Array(9, 10, 11, 12), Array(13, 14, 15, 16))
  • groupBy(func)
//作用:分组,按照传入函数的返回值进行分组。将相同的key对应的值放入一个迭代器

//案例:创建一个RDD,按照元素模以2的值进行分组
val rdd = sc.parallelize(1 to 4)
rdd.groupBy(_%2).collect()
 // Array[(Int, Iterable[Int])] = Array((0,CompactBuffer(2, 4)), (1,CompactBuffer(1, 3)))
  • filter(func)
//作用:过滤。返回一个新的RDD,该RDD由经过func函数计算后返回值为true的输入元素组成

//案例:创建一个RDD(由字符串组成),过滤出一个新RDD(包含”xiao”子串)
val sourceFilter = sc.parallelize(Array("xiaoming","xiaojiang","xiaohe","dazhi"))
  • sample(withReplacement,fraction, seed)
//作用:以指定的随机种子随机抽样出数量为fraction的数据,
//withReplacement表示是抽出的数据是否放回,true为有放回的抽样,false为无放回的抽样,
//seed用于指定随机数生成器种子。

//案例:创建一个RDD(1-10),从中选择放回和不放回抽样
val rdd = sc.parallelize(1 to 10)
rdd.sample(true,0.4,3).collect()//Array[Int] = Array(1, 7, 8, 9, 9, 9)
rdd.sample(false,0.4,3).collect()//Array[Int] = Array(1, 7, 8)
  • coalesce(numPartitions)
//作用:缩减分区数,用于大数据集过滤后,提高小数据集的执行效率

//案例:创建一个4个分区的RDD,对其缩减分区
val rdd = sc.parallelize(1 to 16,4)
rdd.partitions.size //Int = 4
rdd.coalesce(3).partitions.size//Int = 3
  • distinct([numTasks]))
//作用:对源RDD进行去重后返回一个新的RDD。
//默认情况下,只有8个并行任务来操作,但是可以传入一个可选的numTasks参数改变它

//案例:创建一个RDD,使用distinct()对其去重
 val distinctRdd = sc.parallelize(List(1,2,1,5,2,9,6,1))
distinctRdd.distinct().collect()
//Array[Int] = Array(6, 2, 1, 9, 5)
distinctRdd.distinct(3).collect()
//Array[Int] = Array(6, 9, 1, 5, 2)
  • repartition(numPartitions)
//作用:根据分区数,重新通过网络随机洗牌所有数据

//案例:创建一个4个分区的RDD,对其重新分区
val rdd = sc.parallelize(1 to 16,4)
rdd.partitions.size//Int = 4
rdd.repartition(2).partitions.size// Int = 2
  • coalesce和repartition的区别
  • coalesce重新分区,可以选择是否进行shuffle过程。由参数shuffle: Boolean = false/true 决定;
  • repartition实际上是调用的coalesce,默认是进行shuffle的
  • sortBy(func,[ascending],[numTasks])
//作用:使用func先对数据进行处理,按照处理后的数据比较结果排序,默认为正序

//案例:创建一个RDD,按照不同的规则进行排序
val rdd = sc.parallelize(List(2,1,3,4))
rdd.sortBy(x=>x).collect()//Array[Int] = Array(1, 2, 3, 4)
rdd.sortBy(x=>x%3).collect()//Array[Int] = Array(3, 1, 4, 2)
  • Key-Value类型:
  • union(otherDataset)
//作用:对源RDD和参数RDD求并集后返回一个新的RDD

//示例:创建两个RDD,求并集
val rdd1 = sc.parallelize(1 to 5)
val rdd2 = sc.parallelize(3 to 7)
rdd1.union(rdd2).collect()
//Array[Int] = Array(1, 2, 3, 4, 5, 3, 4, 5, 6, 7)
  • subtract(otherDataset)
//作用:计算差的一种函数,去除两个RDD中相同的元素,不同的RDD将保留下来

//示例:创建两个RDD,求第一个RDD与第二个RDD的差集
val rdd1 = sc.parallelize(1 to 5)
val rdd2 = sc.parallelize(3 to 7)
rdd1.subtract(rdd2).collect()
//Array[Int] = Array(2, 1)
  • intersection(otherDataset)
//作用:对源RDD和参数RDD求交集后返回一个新的RDD

//示例:创建两个RDD,求两个RDD的交集
 val rdd1 = sc.parallelize(1 to 5)
 val rdd2 = sc.parallelize(3 to 7)
 rdd1.intersection(rdd2).collect()
 //Array[Int] = Array(4, 3, 5)
  • cartesian(otherDataset)
//作用:笛卡尔积(尽量避免使用)

//示例:创建两个RDD,计算两个RDD的笛卡尔积
val rdd1 = sc.parallelize(1 to 3)
val rdd2 = sc.parallelize(2 to 3)
rdd1.intersection(rdd2).collect()
// Array[(Int, Int)] = Array((1,2), (1,3), (2,2), (3,2), (2,3), (3,3))
  • zip(otherDataset)
//作用:将两个RDD组合成Key/Value形式的RDD,
//这里默认两个RDD的partition数量以及元素数量都相同,否则会抛出异常

//示例:
val rdd1 = sc.parallelize(Array(1,2,3),3)
val rdd2 = sc.parallelize(Array("a","b","c"),3)
rdd1.zip(rdd2).collect
//Array[(Int, String)] = Array((1,a), (2,b), (3,c))