Bootstrap

3)Spark(RDD编程1)

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))
;