RDD编程
在Spark中,RDD被表示为对象,通过对象上的方法调用来对RDD进行转换。经过一系列的transformations定义RDD之后,就可以调用actions触发RDD的计算,action可以是向应用程序返回结果(count, collect等),或者是向存储系统保存数据(saveAsTextFile等)。在Spark中,只有遇到action,才会执行RDD的计算(即延迟计算),这样在运行时可以通过管道的方式传输多个转换。
RDD的创建:
- 集合中创建
- parallelize()
val rdd = sc.parallelize(Array(1,2,3,4,5))
- makeRDD()
val rdd = sc.makeRDD(Array(1,2,3,4,5))
- parallelize()
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)
- map(func):返回一个新的RDD,该RDD由每一个输入元素经过func函数转换后组成
-
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))