Spark Transformation 算子总结笔记


简介:由于本篇文章写的内容多,且杂,加上本人在写的过程中还加入了大量解释,为此本篇文章会设置成粉丝可见,请谅解!


本篇文章主要包含了Spark Transformation 算子的value算子(
1、map()映射
2、mapPartitions()以分区为单位执行Map
map()和mapPartition()的区别:
3、mapPartitionsWithIndex()带分区号
4、flatMap()扁平化
6、groupBy()分组(包含四种方法取首字母)
7、GroupBy()之WordCount
8、filter()过滤
9、sample()采样
10、distinct()去重
11、coalesce()合并分区
12、repartition()重新分区(执行Shuffle)
13、sortBy()排序
14、pipe()调用脚本
)、
双value类型交互算子(
1、intersection()交集
2、union()并集不去重
3、subtract()差集
4、zip()拉链

key-value类型算子(
1、partitionBy()按照K重新分区
2、自定义分区器
3、reduceByKey()按照K聚合V
4、groupByKey()按照K重新分组
reduceByKey和groupByKey区别
5、aggregateByKey()按照K处理分区内和分区间逻辑
6、foldByKey()分区内和分区间相同的aggregateByKey()
7、combineByKey()转换结构后分区内和分区间操作
reduceByKey、foldByKey、aggregateByKey、combineByKey他们之间的区别
8、sortByKey()按照K进行排序
9、mapValues()只对V进行操作
10、 join()等同于sql里的内连接,关联上的要,关联不上的舍弃
11、 cogroup()类似于sql的全连接,但是在同一个RDD中对key聚合
)、
action行动算子(
1、reduce()聚合
2、collect()以数组的形式返回数据集
3、count()返回RDD中元素个数
4、first()返回RDD中的第一个元素
5、take()返回由RDD前n个元素组成的数组
6、takeOrdered()返回该RDD排序后前n个元素组成的数组
7、aggregate()案例
8、fold()案例
9、countByKey()统计每种key的个数
10、save相关算子
11、foreach(f)遍历RDD中每一个元素


目录

value类型

序号名称函数签名功能说明
01map()映射,以行为单位执行def map[U:ClassTag](f:T=>U):RDD[U]参数f是一个函数,它可以接收一个参数。当某个RDD执行map方法时,会遍历RDD中的每一个数据项,并依次应用f函数,从而产生一个新的RDD。即这个新的RD中的每一个元素都是原来RDD中每一个元素依次应用f函数而得到的。
02mapPartitions()以分区为单位执行Mapdef mapPartitions[U:ClassTag](f:Iterator[T]=>Itertor[U], //把f函数每一个分区的数据分别放入到迭代器中,进行批处理
preserversPartitioning:Boolean=false):RDD[U] //preserversPartitioning:是否保留上游RDD的分区信息,默认false
Map是一次处理一个元素,mapPartitions是一次处理一个分区数据。
03mapPartitionsWithIndex()带分区号def mapPartitionsWithIndex[U: ClassTag]( f: (Int, Iterator[T]) => Iterator[U], // Int表示分区编号
preservesPartitioning: Boolean = false): RDD[U]
类似于mapPartitions,比mapPartitions多一个整数参数表示分区号。
04flatMap()扁平化def flatMap[U: ClassTag](f: T => TraversableOnce[U]): RDD[U]与map操作类似,将RDD中的每一个元素通过应用f函数依次转换为新的元素,并封装到RDD中。
区别:在flatMap操作中,f函数的返回值是一个集合,并且会将每一个该集合中的元素拆分出来放到新的RDD中。
05glom()分区转换数组def glom(): RDD[Array[T]]该操作将RDD中每一个分区变成一个数组,并放置在新的RDD中,数组中元素的类型与原分区中元素类型一致。
06groupBy()分组def groupBy[K](f: T => K)(implicit kt: ClassTag[K]): RDD[(K, Iterable[T])]分组,按照传入函数的返回值进行分组。将相同的key对应的值放入一个迭代器。
07filter()过滤def filter(f: T => Boolean): RDD[T]接收一个返回值为布尔类型的函数作为参数。当某个RDD调用filter方法时,会对该RDD中每一个元素应用f函数,如果返回值类型为true,则该元素会被添加到新的RDD中。
08sample()采样def sample(withReplacement: Boolean, fraction: Double,seed: Long = Utils.random.nextLong): RDD[T]// withReplacement: true为有放回的抽样,false为无放回的抽样;// fraction表示:以指定的随机种子随机抽样出数量为fraction的数据;// seed表示:指定随机数生成器种子从大量的数据中采样
09distinct()去重def distinct(): RDD[T] // 默认情况下,distinct会生成与原RDD分区个数一致的分区数

def distinct(numPartitions: Int)(implicit ord: Ordering[T] = null): RDD[T] // 可以去重后修改分区个数

// 3.3 对RDD采用多个Task去重,提高并发度 distinctRdd.distinct(2).collect().foreach(println)
对内部的元素去重,并将去重后的元素放到新的RDD中。
10coalesce()合并分区def coalesce(numPartitions: Int, shuffle: Boolean = false, //默认false不执行shuffle
partitionCoalescer.Option[PartitionCoalescer] = Option.empty)
(implicit ord: Ordering[T] = null) : RDD[T]
缩减分区数,用于大数据集过滤后,提高小数据集的执行效率。
11repartition()重新分区(执行Shuffle)def repartition(numPartitions: Int)(implicit ord: Ordering[T] = null): RDD[T]该操作内部其实执行的是coalesce操作,参数shuffle的默认值为true。无论是将分区数多的RDD转换为分区数少的RDD,还是将分区数少的RDD转换为分区数多的RDD,repartition操作都可以完成,因为无论如何都会经shuffle过程。
12sortBy()排序def sortBy[K]( f: (T) => K,ascending: Boolean = true, // 默认为正序排列
numPartitions: Int = this.partitions.length)(implicit ord: Ordering[K], ctag: ClassTag[K]): RDD[T]
该操作用于排序数据。在排序之前,可以将数据通过f函数进行处理,之后按照f函数处理的结果进行排序,默认为正序排列。排序后新产生的RDD的分区数与原RDD的分区数一致。
13pipe()调用脚本def pipe(command: String): RDD[String]管道,针对每个分区,都调用一次shell脚本,返回输出的RDD。注意:在Worker节点可以访问到的位置脚本需要放

双Value类型交互

序号名称函数签名功能说明
01intersection()交集def intersection(other: RDD[T]): RDD[T]对源RDD和参数RDD求交集后返回一个新的RDD
02union()并集不去重def union(other: RDD[T]): RDD[T]对源RDD和参数RDD求并集后返回一个新的RDD
03subtract()差集def subtract(other: RDD[T]): RDD[T]计算差的一种函数,去除两个RDD中相同元素,不同的RDD将保留下来
04zip()拉链def zip[U: ClassTag](other: RDD[U]): RDD[(T, U)]该操作可以将两个RDD中的元素,以键值对的形式进行合并。其中,键值对中的Key为第1个RDD中的元素,Value为第2个RDD中的元素。将两个RDD组合成Key/Value形式的RDD,这里默认两个RDD的partition数量以及元素数量都相同,否则会抛出异常。

Key-Value类型

序号名称函数签名功能说明
01partitionBy()按照K重新分区def partitionBy(partitioner: Partitioner): RDD[(K, V)]将RDD[K,V]中的K按照指定Partitioner重新进行分区;如果原有的RDD和新的RDD是一致的话就不进行分区,否则会产生Shuffle过程。
02reduceByKey()按照K聚合Vdef reduceByKey(func: (V, V) => V): RDD[(K, V)]
def reduceByKey(func: (V, V) => V, numPartitions: Int): RDD[(K, V)]
该操作可以将RDD[K,V]中的元素按照相同的K对V进行聚合。其存在多种重载形式,还可以设置新RDD的分区数。
03groupByKey()按照K重新分组def groupByKey(): RDD[(K, Iterable[V])]groupByKey对每个key进行操作,但只生成一个seq,并不进行聚合。该操作可以指定分区器或者分区数(默认使用HashPartitioner)
04aggregateByKey()按照K处理分区内和分区间逻辑def aggregateByKey[U: ClassTag](zeroValue: U)(seqOp: (U, V) => U, combOp: (U, U) => U): RDD[(K, U)]
(1)zeroValue(初始值):给每一个分区中的每一种key一个初始值;
(2)seqOp(分区内):函数用于在每一个分区中用初始值逐步迭代value;
(3)combOp(分区间):函数用于合并每个分区中的结果。
按照K处理分区内和分区间逻辑
05foldByKey()分区内和分区间相同的aggregateByKey()def foldByKey(zeroValue: V)(func: (V, V) => V): RDD[(K, V)]
参数zeroValue:是一个初始化值,它可以是任意类型
参数func:是一个函数,两个输入参数相同
aggregateByKey的简化操作,seqop和combop相同。即,分区内逻辑和分区间逻辑相同。
06combineByKey()转换结构后分区内和分区间操作def combineByKey[C](createCombiner: V => C,mergeValue: (C, V) => C,mergeCombiners: (C, C) => C): RDD[(K, C)]
(1)createCombiner(转换数据的结构): combineByKey() 会遍历分区中的所有元素,因此每个元素的键要么还没有遇到过,要么就和之前的某个元素的键相同。如果这是一个新的元素,combineByKey()会使用一个叫作createCombiner()的函数来创建那个键对应的累加器的初始值
(2)mergeValue(分区内): 如果这是一个在处理当前分区之前已经遇到的键,它会使用mergeValue()方法将该键的累加器对应的当前值与这个新的值进行合并
(3)mergeCombiners(分区间): 由于每个分区都是独立处理的,因此对于同一个键可以有多个累加器。如果有两个或者更多的分区都有对应同一个键的累加器,就需要使用用户提供的mergeCombiners()方法将各个分区的结果进行合并。
针对相同K,将V合并成一个集合。
07sortByKey()按照K进行排序def sortByKey(ascending: Boolean = true, // 默认,升序 numPartitions: Int =self.partitions.length) : RDD[(K, V)]在一个(K,V)的RDD上调用,K必须实现Ordered接口,返回一个按照key进行排序的(K,V)的RDD
08mapValues()只对V进行操作def mapValues[U](f: V => U): RDD[(K, U)]针对于(K,V)形式的类型只对V进行操作
09join()等同于sql里的内连接,关联上的要,关联不上的舍弃def join[W](other: RDD[(K, W)]): RDD[(K, (V, W))]
def join[W](other: RDD[(K, W)], numPartitions: Int): RDD[(K, (V, W))]
在类型为(K,V)和(K,W)的RDD上调用,返回一个相同key对应的所有元素对在一起的(K,(V,W))的RDD
10cogroup()类似于sql的全连接,但是在同一个RDD中对key聚合def cogroup[W](other: RDD[(K, W)]): RDD[(K, (Iterable[V], Iterable[W]))]在类型为(K,V)和(K,W)的RDD上调用,返回一个(K,(Iterable,Iterable))类型的RDD操作两个RDD中的KV元素,每个RDD中相同key中的元素分别聚合成一个集合。

Action行动算子

序号名称函数签名功能说明
01reduce()聚合def reduce(f: (T, T) => T): Tf函数聚集RDD中的所有元素,先聚合分区内数据,再聚合分区间数据。
02collect()以数组的形式返回数据集def collect(): Array[T]在驱动程序中,以数组Array的形式返回数据集的所有元素。
03count()返回RDD中元素个数def count(): Long返回RDD中元素的个数
04first()返回RDD中的第一个元素def first(): T返回RDD中的第一个元素
05take()返回由RDD前n个元素组成的数组def take(num: Int): Array[T]返回一个由RDD的前n个元素组成的数组
06takeOrdered()返回该RDD排序后前n个元素组成的数组def takeOrdered(num: Int)(implicit ord: Ordering[T]): Array[T]返回该RDD排序后的前n个元素组成的数组
07aggregate()def aggregate[U: ClassTag](zeroValue: U)(seqOp: (U, T) => U, combOp: (U, U) => U): Uaggregate函数将每个分区里面的元素通过分区内逻辑和初始值进行聚合,然后用分区间逻辑和初始值(zeroValue)进行操作。
08fold()def fold(zeroValue: T)(op: (T, T) => T): T折叠操作,aggregate的简化操作,即,分区内逻辑和分区间逻辑相同
09countByKey()统计每种key的个数def countByKey(): Map[K, Long]统计每种key的个数
10saveAsTextFile(path)保存成Text文件将数据集的元素以textfile的形式保存到HDFS文件系统或者其他支持的文件系统,对于每个元素,Spark将会调用toString方法,将它装换为文件中的文本
11saveAsSequenceFile(path) 保存成Sequencefile文件将数据集中的元素以Hadoop Sequencefile的格式保存到指定的目录下,可以使HDFS或者其他Hadoop支持的文件系统。注意:只有kv类型RDD有该操作,单值的没有
12saveAsObjectFile(path) 序列化成对象保存到文件用于将RDD中的元素序列化成对象,存储到文件中。
13foreach(f)遍历RDD中每一个元素def foreach(f: T => Unit): Unit遍历RDD中的每一个元素,并依次应用f函数

各种算子的相关代码如下

Value类型

1、map()映射

代码如下:

package com.lqs.csdncode.singlevalue

import org.apache.spark.rdd.RDD
import org.apache.spark.{SparkConf, SparkContext}

/**
 * @Author lqs
 * @Date 2022年02月14日 19:55:08
 * @Version 1.0.0
 * @ClassName Test01_map
 * @Describe map 映射
 */
object Test01_map {
  def main(args: Array[String]): Unit = {
    //创建SparkConf并设置App名称
    val conf: SparkConf = new SparkConf().setAppName("SparkCoreTest").setMaster("local[*]")

    //创建SparkContext,该对象是提交SparkApp的入口
    val sc = new SparkContext(conf)

    //使用makeRDD()创建rdd
    val rdd: RDD[Int] = sc.makeRDD(Array(1, 2, 3, 4, 5, 6))

    val rddProduct: RDD[Int] = rdd.map(array => array * 2)
    //简写
    val productRdd: RDD[Int] = rdd.map(_ * 2)

    productRdd.collect().foreach(println)

    //关闭资源
    sc.stop()
  }
}

运行结果如下:

2
4
6
8
10
12

2、mapPartitions()以分区为单位执行Map

代码如下:

package com.lqs.csdncode.singlevalue

import org.apache.spark.{SparkConf, SparkContext}
import org.apache.spark.rdd.RDD

/**
 * @Author lqs
 * @Date 2022年02月14日 20:06:07
 * @Version 1.0.0
 * @ClassName Test02_mapPartitions
 * @Describe mapPartitions()以分区为单位执行Map
 *           Map是一次处理一个元素,而mapPartitions一次处理一个分区数据。
 */
object Test02_mapPartitions {
  def main(args: Array[String]): Unit = {
    //创建SparkConf并设置App名称
    val conf: SparkConf = new SparkConf().setAppName("SparkCoreTest").setMaster("local[*]")

    //创建SparkContext,该对象是提交SparkApp的入口
    val sc = new SparkContext(conf)

    //使用makeRDD()创建rdd
    val rdd: RDD[Int] = sc.makeRDD(Array(1, 2, 3, 4, 5, 6), 3)

    val mptRdd: RDD[Int] = rdd.mapPartitions(x => x.map(array => array * 2))

    //简写版
    val mptRddJ: RDD[Int] = rdd.mapPartitions(_.map(_ * 2))

    mptRddJ.collect().foreach(println)

    //关闭资源
    sc.stop()
  }
}

运行结果如下

2
4
6
8
10
12

map()和mapPartition()的区别:

1、map():每次处理一条数据;
2、mapPartition():每次处理一个分区的数据,这个分区的数据处理完后,原RDD中分区的数据才能释放,可能会导致OOM(内存满后溢出);
3、开发建议:当内存空间较大的时候建议使用mapPartition(),以提高处理效率。

3、mapPartitionsWithIndex()带分区号

代码如下:

package com.lqs.csdncode.singlevalue

import org.apache.spark.{SparkConf, SparkContext}
import org.apache.spark.rdd.RDD

/**
 * @Author lqs
 * @Date 2022年02月14日 20:15:48
 * @Version 1.0.0
 * @ClassName Test03_mapPartitionWithIndex
 * @Describe mapPartitionsWithIndex()带分区号
 *           类似于mapPartitions,比 mapPartitions多一个整数参数表示分区号
 */
object Test03_mapPartitionWithIndex {
  def main(args: Array[String]): Unit = {
    //创建SparkConf并设置App名称
    val conf: SparkConf = new SparkConf().setAppName("SparkCoreTest").setMaster("local[*]")

    //创建SparkContext,该对象是提交SparkApp的入口
    val sc = new SparkContext(conf)

    //使用makeRDD()创建rdd
    val rdd: RDD[Int] = sc.makeRDD(Array(1, 2, 3, 4, 5, 6), 2)

    //创建一个RDD,使每个元素跟所在分区号形成一个元组,组成一个新的RDD
    val mpwiRdd: RDD[(Int, Int)] = rdd.mapPartitionsWithIndex((index, items) => {
      items.map((index, _))
    })

    mpwiRdd.collect().foreach(println)

    //关闭资源
    sc.stop()
  }
}

运行结果如下:

(0,1)
(0,2)
(0,3)
(1,4)
(1,5)
(1,6)

4、flatMap()扁平化

代码如下:

package com.lqs.csdncode.singlevalue

import org.apache.spark.{SparkConf, SparkContext}
import org.apache.spark.rdd.RDD

/**
 * @Author lqs
 * @Date 2022年02月14日 20:22:01
 * @Version 1.0.0
 * @ClassName Test04_flatMap
 * @Describe flatMap()扁平化
 */
object Test04_flatMap {
  def main(args: Array[String]): Unit = {
    //创建SparkConf并设置App名称
    val conf: SparkConf = new SparkConf().setAppName("SparkCoreTest").setMaster("local[*]")

    //创建SparkContext,该对象是提交SparkApp的入口
    val sc = new SparkContext(conf)

    //使用makeRDD()创建rdd
    //    val rdd: RDD[(String, Int)] = sc.makeRDD(List(("a", 88), ("b", 95), ("a", 91), ("b", 93), ("a", 95), ("b", 98)),2)

    val listRDD = sc.makeRDD(List(List(1, 2), List(3, 4), List(5, 6), List(7)), 2)

    //把所有子集合中数据取出放入到一个大的集合中,
    val flatMapAndMapRdd: RDD[Int] = listRDD.flatMap(list => list.map(_ * 2))

    flatMapAndMapRdd.collect().foreach(println)
    println("+++++++++++++++++++++++++++++++++++++++++++++++++++++++")

    val strRDD: RDD[String] = sc.makeRDD(List("hello spark", "zoo hive spark", "hive Hbase"))

    val strFlatMapRdd: RDD[String] = strRDD.flatMap(_.split(" "))

    strFlatMapRdd.collect().foreach(println)
    
    //关闭资源
    sc.stop()
  }
}

运行结果:

2
4
6
8
10
12
14
+++++++++++++++++++++++++++++++++++++++++++++++++++++++
hello
spark
zoo
hive
spark
hive
Hbase

5、glom()分区转换数组
代码如下:

package com.lqs.csdncode.singlevalue

import org.apache.spark.{SparkConf, SparkContext}
import org.apache.spark.rdd.RDD

/**
 * @Author lqs
 * @Date 2022年02月14日 20:48:02
 * @Version 1.0.0
 * @ClassName Test05_glom
 * @Describe glom()分区转换数组
 *           该操作将RDD中每一个分区变成一个数组,并放置在新的RDD中,数组中元素的类型与原分区中元素类型一致
 */
object Test05_glom {
  def main(args: Array[String]): Unit = {
    //创建SparkConf并设置App名称
    val conf: SparkConf = new SparkConf().setAppName("SparkCoreTest").setMaster("local[*]")

    //创建SparkContext,该对象是提交SparkApp的入口
    val sc = new SparkContext(conf)

    //使用makeRDD()创建rdd
    val rdd: RDD[Int] = sc.makeRDD(Array(1, 2, 3, 4, 5, 6), 2)

    //求出每个分区的最大值
    val glomMaxRdd: RDD[Int] = rdd.glom().map(_.max)

    glomMaxRdd.collect().foreach(println)

    //关闭资源
    sc.stop()
  }
}

运行结果如下:

3
6

6、groupBy()分组(包含四种方法取首字母)

package com.lqs.csdncode.singlevalue

import org.apache.spark.{SparkConf, SparkContext}
import org.apache.spark.rdd.RDD

/**
 * @Author lqs
 * @Date 2022年02月14日 20:53:15
 * @Version 1.0.0
 * @ClassName Test06_groupBy
 * @Describe groupBy()分组
 *           按照相同的key将其对应的值放在一个迭代器中
 */
object Test06_groupBy {
  def main(args: Array[String]): Unit = {
    //创建SparkConf并设置App名称
    val conf: SparkConf = new SparkConf().setAppName("SparkCoreTest").setMaster("local[*]")

    //创建SparkContext,该对象是提交SparkApp的入口
    val sc = new SparkContext(conf)

    //使用makeRDD()创建rdd
    val rdd: RDD[Int] = sc.makeRDD(Array(1, 2, 3, 4, 5, 6), 2)

    //将每个分区的数据放到一个数组并手机到Driver端打印
    val groupByRdd: RDD[(Int, Iterable[Int])] = rdd.groupBy(_ % 2)
    groupByRdd.collect().foreach(println)

    println("***********************************")

    //创建一个RDD
    val rdd1: RDD[String] = sc.makeRDD(List("hello", "hive", "hadoop", "spark", "scala"))

    //按照首字母第一个单词相同分组
    //方法一
    val groupBySubstringRdd: RDD[(String, Iterable[String])] = rdd1.groupBy(_.substring(0, 1))
    groupBySubstringRdd.collect().foreach(println)
    println("***********************************")

    //方法二
    val groupByCharAtRdd: RDD[(Char, Iterable[String])] = rdd1.groupBy(_.charAt(0))
    groupByCharAtRdd.collect().foreach(println)
    println("***********************************")

    //方法三
    val groupByHeadRdd: RDD[(Char, Iterable[String])] = rdd1.groupBy(_.head)
    groupByHeadRdd.collect().foreach(println)
    println("***********************************")

    //方法四
    val groupTakeRdd: RDD[(String, Iterable[String])] = rdd1.groupBy(_.take(1))
    groupTakeRdd.collect().foreach(println)
    println("***********************************")
    
    //关闭资源
    sc.stop()
  }
}

运行结果:

(0,CompactBuffer(2, 4, 6))
(1,CompactBuffer(1, 3, 5))
***********************************
(s,CompactBuffer(spark, scala))
(h,CompactBuffer(hello, hive, hadoop))
***********************************
(s,CompactBuffer(spark, scala))
(h,CompactBuffer(hello, hive, hadoop))
***********************************
(s,CompactBuffer(spark, scala))
(h,CompactBuffer(hello, hive, hadoop))
***********************************
(s,CompactBuffer(spark, scala))
(h,CompactBuffer(hello, hive, hadoop))

7、GroupBy()之WordCount

package com.lqs.csdncode.singlevalue

import org.apache.spark.{SparkConf, SparkContext}
import org.apache.spark.rdd.RDD

/**
 * @Author lqs
 * @Date 2022年02月14日 21:07:33
 * @Version 1.0.0
 * @ClassName Test07_groupBy_wordCount
 * @Describe Spark之WordCount案例
 */
object Test07_groupBy_wordCount {
  def main(args: Array[String]): Unit = {
    //创建SparkConf并设置App名称
    val conf: SparkConf = new SparkConf().setAppName("SparkCoreTest").setMaster("local[*]")

    //创建SparkContext,该对象是提交SparkApp的入口
    val sc = new SparkContext(conf)

    //使用makeRDD()创建rdd
    val strList: List[String] = List("Hello Scala", "Hello Spark", "Hello World", "Hello Java", "Hello Python")
    val rdd: RDD[String] = sc.makeRDD(strList)

    //将字符串拆分成一个个的单词
    val flatMapRdd: RDD[String] = rdd.flatMap(_.split(" "))

    //将拆分后的单词进行转换:word=>(word,1)
    //    val mapRdd: RDD[(String, Int)] = flatMapRdd.map(fmr => (fmr, 1))
    val mapRdd: RDD[(String, Int)] = flatMapRdd.map((_, 1))

    //将转换结构后的数据进行分组
    val groupRdd: RDD[(String, Iterable[(String, Int)])] = mapRdd.groupBy(mr => mr._1)

    //将分组后的的数据进行结构的转换
    //方法一
    //    val wordToSum: RDD[(String, Int)] = groupRdd.map(gr => (gr._1, gr._2.toList.size))
    //    方法二,模式匹配
    //    val wordTosum: RDD[(String, Int)] = groupRdd.map {
    //      x =>
    //        x match {
    //          case (word, list) => {
    //            (word, list.size)
    //          }
    //        }
    //    }

    //    方法三:偏函数
    val wordToSum: RDD[(String, Int)] = groupRdd.map {
      case (word, list) => {
        (word, list.size)
      }
    }

    wordToSum.collect().foreach(println)

    //关闭资源
    sc.stop()
  }
}

运行结果:

(Hello,5)
(Java,1)
(World,1)
(Scala,1)
(Python,1)
(Spark,1)

8、filter()过滤

代码如下:

package com.lqs.csdncode.singlevalue

import org.apache.spark.{SparkConf, SparkContext}
import org.apache.spark.rdd.RDD

/**
 * @Author lqs
 * @Date 2022年02月14日 21:25:52
 * @Version 1.0.0
 * @ClassName Test08_filter
 * @Describe filter()过滤
 */
object Test08_filter {
  def main(args: Array[String]): Unit = {
    //创建SparkConf并设置App名称
    val conf: SparkConf = new SparkConf().setAppName("SparkCoreTest").setMaster("local[*]")

    //创建SparkContext,该对象是提交SparkApp的入口
    val sc = new SparkContext(conf)

    //使用makeRDD()创建rdd
    val rdd: RDD[Int] = sc.makeRDD(Array(1, 2, 3, 4, 5, 6), 2)

    //过滤出符合条件的数据
    val filterRdd: RDD[Int] = rdd.filter(_ % 2 == 0)

    filterRdd.collect().foreach(println)

    //关闭资源
    sc.stop()
  }
}

运行结果如下:

2
4
6

9、sample()采样

代码如下:

package com.lqs.csdncode.singlevalue

import org.apache.spark.{SparkConf, SparkContext}
import org.apache.spark.rdd.RDD

/**
 * @Author lqs
 * @Date 2022年02月14日 21:27:50
 * @Version 1.0.0
 * @ClassName Test09_sample
 * @Describe sample()采样
 */
object Test09_sample {
  def main(args: Array[String]): Unit = {
    //创建SparkConf并设置App名称
    val conf: SparkConf = new SparkConf().setAppName("SparkCoreTest").setMaster("local[*]")

    //创建SparkContext,该对象是提交SparkApp的入口
    val sc = new SparkContext(conf)

    //使用makeRDD()创建rdd
    val rdd: RDD[Int] = sc.makeRDD(Array(1, 2, 3, 4, 5, 6))

    /*
    抽取数据不放回(伯努利算法)
    伯努利算法:又叫0、1分布。例如扔硬币,要么正面,要么反面。
    具体实现:根据种子和随机算法算出一个数和第二个参数设置几率比较,小于第二个参数要,大于不要
    第一个参数:抽取的数据是否放回,false:不放回
    第二个参数:抽取的几率,范围在[0,1]之间,0:全不取;1:全取;
    第三个参数:随机数种子
     */
    val sampleRdd: RDD[Int] = rdd.sample(false, 0.5)
    sampleRdd.collect().foreach(println)

    println("+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++")

    /*
    抽取数据放回(泊松算法)
    第一个参数:抽取的数据是否放回,true:放回;false:不放回
    第二个参数:重复数据的几率,范围大于等于0.表示每一个元素被期望抽取到的次数
    第三个参数:随机数种子
     */
    val sampleRdd01: RDD[Int] = rdd.sample(true, 1)
    sampleRdd01.collect().foreach(println)

    //关闭资源
    sc.stop()
  }
}

运行结果如下

2
6
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1
2
2
3
3
3
5

10、distinct()去重

代码如下:

package com.lqs.csdncode.singlevalue

import org.apache.spark.rdd.RDD
import org.apache.spark.{SparkConf, SparkContext}

/**
 * @Author lqs
 * @Date 2022年02月16日 19:38:20
 * @Version 1.0.0
 * @ClassName Test10_distinct
 * @Describe distinct()去重
 */
object Test10_distinct {
  def main(args: Array[String]): Unit = {
    //创建SparkConf并设置App名称
    val conf: SparkConf = new SparkConf().setAppName("SparkCoreTest").setMaster("local[*]")

    //创建SparkContext,该对象是提交SparkApp的入口
    val sc = new SparkContext(conf)

    //使用makeRDD()创建rdd
    val rdd: RDD[Int] = sc.makeRDD(Array(1, 2, 3, 4, 5, 6))

    //打印去重后生成的新Rdd
    val distinctRdd: RDD[Int] = rdd.distinct()
    distinctRdd.collect().foreach(println)
    println("#############################################")

    //对rdd采用多个Task去重,提供并发度
    val distinctRdd1: RDD[Int] = rdd.distinct(3)
    distinctRdd1.collect().foreach(println)

    //关闭资源
    sc.stop()
  }
}

运行结果如下:

1
2
3
4
5
6
#############################################
6
3
4
1
5
2

11、coalesce()合并分区

代码如下:

package com.lqs.csdncode.singlevalue

import org.apache.spark.{SparkConf, SparkContext}
import org.apache.spark.rdd.RDD

/**
 * @Author lqs
 * @Date 2022年02月16日 19:43:48
 * @Version 1.0.0
 * @ClassName Test11_coalesce
 * @Describe coalesce()合并分区
 *           注意:
 *           Coalesce算子包括:配置执行Shuffle和配置不执行Shuffle两种方式。
 */
object Test11_coalesce {
  def main(args: Array[String]): Unit = {
    //创建SparkConf并设置App名称
    val conf: SparkConf = new SparkConf().setAppName("SparkCoreTest").setMaster("local[*]")

    //创建SparkContext,该对象是提交SparkApp的入口
    val sc = new SparkContext(conf)

    //使用makeRDD()创建rdd
    val rdd: RDD[Int] = sc.makeRDD(Array(1, 2, 3, 4, 5, 6), 6)

    //缩减分区
    val coalesceRdd: RDD[Int] = rdd.coalesce(2)

    //查看分区数据
    coalesceRdd.mapPartitionsWithIndex((num, list) => list.map((num, _)))
      .collect().foreach(println)
    println("#############################################")

    /*
    执行Shuffle
     */
    val coalesceShuffleRdd: RDD[Int] = rdd.coalesce(1, true)
    coalesceRdd.mapPartitionsWithIndex((num, list) => list.map((num, _)))
      .collect().foreach(println)

    //8 延迟一段时间,观察http://localhost:4040,我的是http://lqs:4040页面,查看Shuffle读写数据
    Thread.sleep(600000)

    //关闭资源
    sc.stop()
  }
}

运行结果如下
(0,1)
(0,2)
(0,3)
(1,4)
(1,5)
(1,6)
#############################################
(0,1)
(0,2)
(0,3)
(1,4)
(1,5)
(1,6)
不走Shuffle截图:
在这里插入图片描述

走Shuffle截图:
在这里插入图片描述

12、repartition()重新分区(执行Shuffle)

代码如下:

package com.lqs.csdncode.singlevalue

import org.apache.spark.rdd.RDD
import org.apache.spark.{SparkConf, SparkContext}

/**
 * @Author lqs
 * @Date 2022年02月16日 19:55:22
 * @Version 1.0.0
 * @ClassName Test12_repartiton
 * @Describe repartition()重新分区(执行Shuffle)
 */
object Test12_repartiton {
  def main(args: Array[String]): Unit = {
    //创建SparkConf并设置App名称
    val conf: SparkConf = new SparkConf().setAppName("SparkCoreTest").setMaster("local[*]")

    //创建SparkContext,该对象是提交SparkApp的入口
    val sc = new SparkContext(conf)

    //使用makeRDD()创建rdd
    val rdd: RDD[Int] = sc.makeRDD(Array(1, 2, 3, 4, 5, 6), 6)

    //缩减分区,走Shuffle
    val coalesceRdd: RDD[Int] = rdd.coalesce(1, true)
    coalesceRdd.mapPartitionsWithIndex((num, list) => list.map((num, _)))
      .collect().foreach(println)

    println("#############################################")

    //重新分区,走Shuffle
    val repartitionRdd: RDD[Int] = coalesceRdd.repartition(3)
    repartitionRdd.mapPartitionsWithIndex((num, list) => list.map((num, _)))
      .collect().foreach(println)

    Thread.sleep(6666666)

    //关闭资源
    sc.stop()
  }
}

运行结果如下:

(0,1)
(0,2)
(0,3)
(0,4)
(0,5)
(0,6)
#############################################
(0,3)
(0,6)
(1,1)
(1,4)
(2,2)
(2,5)

repartition走shuffle截图
在这里插入图片描述

13、sortBy()排序

代码如下:

package com.lqs.csdncode.singlevalue

import org.apache.spark.rdd.RDD
import org.apache.spark.{SparkConf, SparkContext}

/**
 * @Author lqs
 * @Date 2022年02月16日 20:02:55
 * @Version 1.0.0
 * @ClassName Test13_sortBy
 * @Describe sortBy()排序
 */
object Test13_sortBy {
  def main(args: Array[String]): Unit = {
    //创建SparkConf并设置App名称
    val conf: SparkConf = new SparkConf().setAppName("SparkCoreTest").setMaster("local[*]")

    //创建SparkContext,该对象是提交SparkApp的入口
    val sc = new SparkContext(conf)

    //使用makeRDD()创建rdd
    val rdd: RDD[Int] = sc.makeRDD(Array(1, 2, 3, 4, 5, 6))

    //默认是升序进行排序
    val sortByRdd: RDD[Int] = rdd.sortBy(number => number)
    sortByRdd.collect().foreach(println)

    println("#############################################")

    //配置为倒序排序
    rdd.sortBy(number => number, false).collect().foreach(println)

    println("#############################################")

    val strRdd: RDD[String] = sc.makeRDD(List("2", "28", "12", "6", "32"))
    //按照字符的int值进行排序
    strRdd.sortBy(number => number.toInt).collect().foreach(println)

    println("#############################################")

    val rdd3: RDD[(Int, Int)] = sc.makeRDD(List((2, 1), (1, 2), (1, 1), (2, 2)))
    //先按照tuple第一个值进行排序,在相等的情况下按照第二个值进行排序
    rdd3.sortBy(str => str).collect().foreach(println)

    //关闭资源
    sc.stop()
  }
}

运行结果如下:

1
2
3
4
5
6
#############################################
6
5
4
3
2
1
#############################################
2
6
12
28
32
#############################################
(1,1)
(1,2)
(2,1)
(2,2)

14、pipe()调用脚本

(1)编写一个脚本,并增加执行权限

[[lqs@bdc112 spark-local]$ vim pipe.sh

#!/bin/bash

echo "Start"
while read LINE; do
   echo ">>>"${LINE}
done

[lqs@bdc112 spark-local]$ chmod 777 pipe.sh

(2)创建一个只有一个分区的RDD

[[lqs@bdc112 spark-local]$ bin/spark-shell
scala> val rdd = sc.makeRDD (List("hi","Hello","how","are","you"), 1)

(3)将脚本作用该RDD并打印

scala> rdd.pipe("/home/lqs/spark-local/pipe.sh").collect()
res18: Array[String] = Array(Start, >>>hi, >>>Hello, >>>how, >>>are, >>>you)

(4)创建一个有两个分区的RDD

scala> val rdd = sc.makeRDD(List("hi","Hello","how","are","you"), 2)

(5)将脚本作用该RDD并打印

scala> rdd.pipe("/home/lqs/module/spark-local/pipe.sh").collect()
res19: Array[String] = Array(Start, >>>hi, >>>Hello, Start, >>>how, >>>are, >>>you)

注意:一个分区调用一次脚本。

双Value类型交互

1、intersection()交集

代码如下:

package com.lqs.csdncode.doublevalue

import org.apache.spark.{SparkConf, SparkContext}
import org.apache.spark.rdd.RDD

/**
 * @Author lqs
 * @Date 2022年02月16日 20:39:33
 * @Version 1.0.0
 * @ClassName Test01_intersection
 * @Describe intersection()交集
 */
object Test01_intersection {
  def main(args: Array[String]): Unit = {
    //创建SparkConf并设置App名称
    val conf: SparkConf = new SparkConf().setAppName("SparkCoreTest").setMaster("local[*]")

    //创建SparkContext,该对象是提交SparkApp的入口
    val sc = new SparkContext(conf)

    //使用makeRDD()创建rdd
    val rdd: RDD[Int] = sc.makeRDD(Array(1, 2, 3, 4, 5, 6))
    val rdd1: RDD[Int] = sc.makeRDD(1 to 4)

    //计算第一个rdd与第二个rdd的交集并打印
    rdd.intersection(rdd1).collect().foreach(println)

    //关闭资源
    sc.stop()
  }
}

运行结果如下:

1
2
3
4

2、union()并集不去重

代码如下

package com.lqs.csdncode.doublevalue

import org.apache.spark.{SparkConf, SparkContext}
import org.apache.spark.rdd.RDD

/**
 * @Author lqs
 * @Date 2022年02月16日 20:42:34
 * @Version 1.0.0
 * @ClassName Test02_union
 * @Describe union()并集不去重
 */
object Test02_union {
  def main(args: Array[String]): Unit = {
    //创建SparkConf并设置App名称
    val conf: SparkConf = new SparkConf().setAppName("SparkCoreTest").setMaster("local[*]")

    //创建SparkContext,该对象是提交SparkApp的入口
    val sc = new SparkContext(conf)

    //使用makeRDD()创建rdd
    val rdd: RDD[Int] = sc.makeRDD(Array(1, 2, 3, 4, 5, 6))
    val rdd1: RDD[Int] = sc.makeRDD(1 to 8)

    rdd.union(rdd1).collect().foreach(println)

    //关闭资源
    sc.stop()
  }
}

运行结果如下

1
2
3
4
5
6
1
2
3
4
5
6
7
8

3、subtract()差集

代码如下

package com.lqs.csdncode.doublevalue

import org.apache.spark.rdd.RDD
import org.apache.spark.{SparkConf, SparkContext}

/**
 * @Author lqs
 * @Date 2022年02月16日 20:45:27
 * @Version 1.0.0
 * @ClassName Test03_subtract
 * @Describe subtract()取差集
 */
object Test03_subtract {
  def main(args: Array[String]): Unit = {
    //创建SparkConf并设置App名称
    val conf: SparkConf = new SparkConf().setAppName("SparkCoreTest").setMaster("local[*]")

    //创建SparkContext,该对象是提交SparkApp的入口
    val sc = new SparkContext(conf)

    //使用makeRDD()创建rdd
    val rdd: RDD[Int] = sc.makeRDD(Array(1, 2, 3, 4, 5, 6, 7, 8, 9))
    val rdd1: RDD[Int] = sc.makeRDD(6 to 12)

    rdd.subtract(rdd1).collect().foreach(println)

    //关闭资源
    sc.stop()
  }
}

运行结果如下

1
2
3
4
5

4、zip()拉链

代码如下

package com.lqs.csdncode.doublevalue

import org.apache.spark.rdd.RDD
import org.apache.spark.{SparkConf, SparkContext}

/**
 * @Author lqs
 * @Date 2022年02月16日 20:48:02
 * @Version 1.0.0
 * @ClassName Test04_zip
 * @Describe zip()拉链
 */
object Test04_zip {
  def main(args: Array[String]): Unit = {
    //创建SparkConf并设置App名称
    val conf: SparkConf = new SparkConf().setAppName("SparkCoreTest").setMaster("local[*]")

    //创建SparkContext,该对象是提交SparkApp的入口
    val sc = new SparkContext(conf)

    //使用makeRDD()创建rdd
    /*
    注意两个Rdd的数据的长度必须一致,否则会报错
     */
    val rdd: RDD[Int] = sc.makeRDD(Array(1, 2, 3, 4, 5, 6))
    val rdd1: RDD[Int] = sc.makeRDD(Array(1, 2, 3, 4, 5, 6))

    rdd.zip(rdd1).collect().foreach(println)

    //关闭资源
    sc.stop()
  }
}

运行结果如下

(1,1)
(2,2)
(3,3)
(4,4)
(5,5)
(6,6)

Key-Value类型

1、partitionBy()按照K重新分区

代码如下

package com.lqs.csdncode.keyvalue

import org.apache.spark.rdd.RDD
import org.apache.spark.{HashPartitioner, SparkConf, SparkContext}

/**
 * @Author lqs
 * @Date 2022年02月16日 20:54:01
 * @Version 1.0.0
 * @ClassName Test01_partitionBy
 * @Describe partitionBy()按照K重新分区
 */
object Test01_partitionBy {
  def main(args: Array[String]): Unit = {
    //创建SparkConf并设置App名称
    val conf: SparkConf = new SparkConf().setAppName("SparkCoreTest").setMaster("local[*]")

    //创建SparkContext,该对象是提交SparkApp的入口
    val sc = new SparkContext(conf)

    //使用makeRDD()创建rdd
    val rdd: RDD[(String, Int)] = sc.makeRDD(List(("a", 88), ("b", 95), ("a", 91), ("b", 93), ("a", 95), ("b", 98)), 2)

    //对rdd重新分区
    val partitionByRdd: RDD[(String, Int)] = rdd.partitionBy(new HashPartitioner(3))
    partitionByRdd.mapPartitionsWithIndex((num, list) => list.map((num, _)))
      .collect().foreach(println)

    //关闭资源
    sc.stop()
  }
}

运行结果如下

(1,(a,88))
(1,(a,91))
(1,(a,95))
(2,(b,95))
(2,(b,93))
(2,(b,98))

2、自定义分区器

代码如下

package com.lqs.csdncode.keyvalue

import org.apache.spark.rdd.RDD
import org.apache.spark.{HashPartitioner, Partitioner, SparkConf, SparkContext}

/**
 * @Author lqs
 * @Date 2022年02月16日 20:54:01
 * @Version 1.0.0
 * @ClassName Test01_partitionBy
 * @Describe partitionBy()按照K重新分区
 *           之自定义分区器
 */
object Test01_partitionByCustomize {
  def main(args: Array[String]): Unit = {
    //创建SparkConf并设置App名称
    val conf: SparkConf = new SparkConf().setAppName("SparkCoreTest").setMaster("local[*]")

    //创建SparkContext,该对象是提交SparkApp的入口
    val sc = new SparkContext(conf)

    //使用makeRDD()创建rdd
    val rdd: RDD[(Int, String)] = sc.makeRDD(Array((1, "aaa"), (2, "bbb"), (3, "ccc")), 3)

    //对rdd重新分区
    val partitionByRdd: RDD[(Int, String)] = rdd.partitionBy(new MyPartitioner(2))
    partitionByRdd.mapPartitionsWithIndex((num, list) => list.map((num, _)))
      .collect().foreach(println)

    //关闭资源
    sc.stop()
  }
}

/**
 * 自定义分区器
 */
class MyPartitioner(number: Int) extends Partitioner {
  //设置分区数
  override def numPartitions: Int = number

  //具体分区逻辑
  override def getPartition(key: Any): Int = {
    if (key.isInstanceOf[Int]) {
      val i: Int = key.asInstanceOf[Int]
      if (i % 2 == 0) {
        0
      } else {
        1
      }
    } else {
      0
    }
  }
}

运行结果如下

(0,(2,bbb))
(1,(1,aaa))
(1,(3,ccc))

3、reduceByKey()按照K聚合V

代码如下

package com.lqs.csdncode.keyvalue

import org.apache.spark.rdd.RDD
import org.apache.spark.{SparkConf, SparkContext}

/**
 * @Author lqs
 * @Date 2022年02月16日 21:14:55
 * @Version 1.0.0
 * @ClassName Test03_reduceByKey
 * @Describe reduceByKey()按照K聚合V
 */
object Test03_reduceByKey {
  def main(args: Array[String]): Unit = {
    //创建SparkConf并设置App名称
    val conf: SparkConf = new SparkConf().setAppName("SparkCoreTest").setMaster("local[*]")

    //创建SparkContext,该对象是提交SparkApp的入口
    val sc = new SparkContext(conf)

    //使用makeRDD()创建rdd
    val rdd: RDD[(String, Int)] = sc.makeRDD(List(("a", 88), ("b", 95), ("a", 91), ("b", 93), ("a", 95), ("b", 98)), 2)

    //计算相同key对应值的相加结果
    rdd.reduceByKey(_ + _).collect().foreach(println)

    //关闭资源
    sc.stop()
  }
}

运行结果如下

(b,286)
(a,274)

代码如下

运行结果如下

4、groupByKey()按照K重新分组

代码如下

package com.lqs.csdncode.keyvalue

import org.apache.spark.rdd.RDD
import org.apache.spark.{SparkConf, SparkContext}

/**
 * @Author lqs
 * @Date 2022年02月16日 21:17:34
 * @Version 1.0.0
 * @ClassName Test04_groupByKey
 * @Describe
 */
object Test04_groupByKey {
  def main(args: Array[String]): Unit = {
    //创建SparkConf并设置App名称
    val conf: SparkConf = new SparkConf().setAppName("SparkCoreTest").setMaster("local[*]")

    //创建SparkContext,该对象是提交SparkApp的入口
    val sc = new SparkContext(conf)

    //使用makeRDD()创建rdd
    val rdd: RDD[(String, Int)] = sc.makeRDD(List(("a", 88), ("b", 95), ("a", 91), ("b", 93), ("a", 95), ("b", 98)), 2)

    //将相同key对应值聚合到一个Seq中
    val groupByKeyRdd: RDD[(String, Iterable[Int])] = rdd.groupByKey()

    groupByKeyRdd.collect().foreach(println)

    println("###########################################################")

    //计算相同key对应值的相加结果
    groupByKeyRdd.map(g => (g._1, g._2.size)).collect().foreach(println)

    //关闭资源
    sc.stop()
  }
}

运行结果如下

(b,CompactBuffer(95, 93, 98))
(a,CompactBuffer(88, 91, 95))
###########################################################
(b,3)
(a,3)

reduceByKey和groupByKey区别

1、reduceByKey:按照key进行聚合,在shuffle之前有combine(预聚合)操作,返回结果是RDD[K,V]。
2、groupByKey:按照key进行分组,直接进行shuffle。
3、开发指导:在不影响业务逻辑的前提下,优先选用reduceByKey。求和操作不影响业务逻辑,求平均值影响业务逻辑。

5、aggregateByKey()按照K处理分区内和分区间逻辑

代码如下

package com.lqs.csdncode.keyvalue

import org.apache.spark.{SparkConf, SparkContext}
import org.apache.spark.rdd.RDD

/**
 * @Author lqs
 * @Date 2022年02月16日 21:23:38
 * @Version 1.0.0
 * @ClassName Test05_aggregateByKey
 * @Describe aggregateByKey()按照K处理分区内和分区间逻辑
 */
object Test05_aggregateByKey {
  def main(args: Array[String]): Unit = {
    //创建SparkConf并设置App名称
    val conf: SparkConf = new SparkConf().setAppName("SparkCoreTest").setMaster("local[*]")

    //创建SparkContext,该对象是提交SparkApp的入口
    val sc = new SparkContext(conf)

    //使用makeRDD()创建rdd
    val rdd: RDD[(String, Int)] = sc.makeRDD(List(("a", 88), ("b", 95), ("a", 91), ("b", 93), ("a", 95), ("b", 98)),2)

    //取出每个分区相同key对应值的最大值,然后相加
    rdd.aggregateByKey(0)(math.max(_,_),_+_).collect().foreach(println)

    println("###########################################################")

    rdd.aggregateByKey(100)(math.max(_,_),_+_).collect().foreach(println)

    //关闭资源
    sc.stop()
  }
}

运行结果如下

(b,193)
(a,186)
###########################################################
(b,200)
(a,200)

6、foldByKey()分区内和分区间相同的aggregateByKey()

代码如下

package com.lqs.csdncode.keyvalue

import org.apache.spark.{SparkConf, SparkContext}
import org.apache.spark.rdd.RDD

/**
 * @Author lqs
 * @Date 2022年02月16日 21:28:11
 * @Version 1.0.0
 * @ClassName Test06_foldByKey
 * @Describe foldByKey()分区内和分区间相同的aggregateByKey()
 */
object Test06_foldByKey {
  def main(args: Array[String]): Unit = {
    //创建SparkConf并设置App名称
    val conf: SparkConf = new SparkConf().setAppName("SparkCoreTest").setMaster("local[*]")

    //创建SparkContext,该对象是提交SparkApp的入口
    val sc = new SparkContext(conf)

    //使用makeRDD()创建rdd
    val rdd: RDD[(String, Int)] = sc.makeRDD(List(("a", 88), ("b", 95), ("a", 91), ("b", 93), ("a", 95), ("b", 98)), 2)

    rdd.aggregateByKey(0)(_ + _, _ + _).collect().foreach(println)

    println("###########################################################")

    rdd.foldByKey(0)(_ + _).collect().foreach(println)

    //关闭资源
    sc.stop()
  }
}

运行结果如下

(b,286)
(a,274)
###########################################################
(b,286)
(a,274)

7、combineByKey()转换结构后分区内和分区间操作

代码如下

package com.lqs.csdncode.keyvalue

import org.apache.spark.{SparkConf, SparkContext}
import org.apache.spark.rdd.RDD

/**
 * @Author lqs
 * @Date 2022年02月16日 23:23:55
 * @Version 1.0.0
 * @ClassName Test07_combineByKey
 * @Describe combineByKey()转换结构后分区内和分区间操作
 *
 *           需求说明:
 *           创建一个pairRDD,根据key计算每种key的平均值。(先计算每个key出现的次数以及可以对应值的总和,再相除得到结果)
 */
object Test07_combineByKey {
  def main(args: Array[String]): Unit = {
    //创建SparkConf并设置App名称
    val conf: SparkConf = new SparkConf().setAppName("SparkCoreTest").setMaster("local[*]")

    //创建SparkContext,该对象是提交SparkApp的入口
    val sc = new SparkContext(conf)

    //使用makeRDD()创建rdd
    val rdd: RDD[(String, Int)] = sc.makeRDD(List(("a", 88), ("b", 95), ("a", 91), ("b", 93), ("a", 95), ("b", 98)), 2)

    //将相同key对应的值相加,同时记录key出现的次数,放入一个二元组
    //对其规约成(单词,(sum,274))
    val combineRdd: RDD[(String, (Int, Int))] = rdd.combineByKey(
      //("a", 88)=>(88,1)
      //      value=>(value,1),
      (_, 1),

      /*
      分区内聚合相加
      分区1:("a", 88), ("b", 95), ("a", 91),处理过程a:(88,1)+(91,1)=>(88+91,1+1)=>(179,2),处理过程b:(95,1)=>(95,1)
      分区2:("b", 93), ("a", 95), ("b", 98), 处理过程a:(95,1)=>(95,1),处理过程b:(93,1)+(98,1)=>(93+98,1+1)=>(191,2)
      */
      (acc: (Int, Int), value) => (acc._1 + value, acc._2 + 1),

      /*
      分区间聚合相加
      分区1+分区2:
          分区1 b:(95,1)+(191,2)=>(95+191,1+2)=>(286,3)
          分区2 a:(179,2)+(95,1)=>(179+95,2+1)=>(274,3)

      最终结果:
          分区1:(286,3)=>(b,(286,3))
          分区2:(274,3)=>(a,(274,3))
       */
      (acc1: (Int, Int), acc2: (Int, Int)) => (acc1._1 + acc2._1, acc1._2 + acc2._2)
    )

    combineRdd.collect().foreach(println)

    println("###########################################################")

    //计算平均值
    combineRdd.map {
      case (key, value) => {
        (key, value._1 / value._2)
      }
    }.collect().foreach(println)

    println("###########################################################")

    //对rdd进行求积(单词,("product",274))
    val combineRddProduct: RDD[(String, (String, Int))] = rdd.combineByKey(
      //      i => ("product", i),
      ("product", _),
      (res: (String, Int), elem: Int) => (res._1, res._2 * elem),
      (res: (String, Int), elem: (String, Int)) => (res._1, res._2 * elem._2)
    )

    combineRddProduct.collect().foreach(println)

    //关闭资源
    sc.stop()
  }
}

运行结果如下

(b,(286,3))
(a,(274,3))
###########################################################
(b,95)
(a,91)
###########################################################
(b,(product,865830))
(a,(product,760760))

reduceByKey、foldByKey、aggregateByKey、combineByKey他们之间的区别
key-value算子名区别
reduceByKey没有初始值,分区内核分区间计算逻辑一致
foldByKey有初始值,分区内和分区间计算逻辑一致
aggregateByKey有初始值,分区内和分区间计算逻辑还可以变,从而更加的灵活
combineByKey有初始值,并且初始值还支持改变数据结构,是最灵活的,但同时也是他们四个中最底层的

8、sortByKey()按照K进行排序

代码如下

package com.lqs.csdncode.keyvalue

import org.apache.spark.{SparkConf, SparkContext}
import org.apache.spark.rdd.RDD

/**
 * @Author lqs
 * @Date 2022年02月17日 10:18:15
 * @Version 1.0.0
 * @ClassName Test08_sortByKey
 * @Describe sortByKey()按照K进行排序
 */
object Test08_sortByKey {
  def main(args: Array[String]): Unit = {
    //创建SparkConf并设置App名称
    val conf: SparkConf = new SparkConf().setAppName("SparkCoreTest").setMaster("local[*]")

    //创建SparkContext,该对象是提交SparkApp的入口
    val sc = new SparkContext(conf)

    //使用makeRDD()创建rdd
    val rdd: RDD[(Int, String)] = sc.makeRDD(Array((3, "aa"), (6, "cc"), (2, "bb"), (1, "dd")))

    //按照key的正序(默认排序)进行排序
    rdd.sortByKey(true).collect().foreach(println)

    println("###########################################################")

    //按照key倒序排序
    rdd.sortByKey(false).collect().foreach(println)

    //关闭资源
    sc.stop()
  }
}

运行结果如下

(1,dd)
(2,bb)
(3,aa)
(6,cc)
###########################################################
(6,cc)
(3,aa)
(2,bb)
(1,dd)

9、mapValues()只对V进行操作

代码如下

package com.lqs.csdncode.keyvalue

import org.apache.spark.rdd.RDD
import org.apache.spark.{SparkConf, SparkContext}

/**
 * @Author lqs
 * @Date 2022年02月17日 10:33:33
 * @Version 1.0.0
 * @ClassName Test09_mapValues
 * @Describe mapValues()只对V进行操作
 */
object Test09_mapValues {
  def main(args: Array[String]): Unit = {
    //创建SparkConf并设置App名称
    val conf: SparkConf = new SparkConf().setAppName("SparkCoreTest").setMaster("local[*]")

    //创建SparkContext,该对象是提交SparkApp的入口
    val sc = new SparkContext(conf)

    //使用makeRDD()创建rdd
    val rdd: RDD[(Int, String)] = sc.makeRDD(Array((3, "aa"), (6, "cc"), (2, "bb"), (1, "dd")))

    //对value添加字符串"_+_"
    rdd.mapValues(_ + "_+_").collect().foreach(println)

    //关闭资源
    sc.stop()
  }
}

运行结果如下

(3,aa_+_)
(6,cc_+_)
(2,bb_+_)
(1,dd_+_)

10、 join()等同于sql里的内连接,关联上的要,关联不上的舍弃

代码如下

package com.lqs.csdncode.keyvalue

import org.apache.spark.{SparkConf, SparkContext}
import org.apache.spark.rdd.RDD

/**
 * @Author lqs
 * @Date 2022年02月17日 10:36:45
 * @Version 1.0.0
 * @ClassName Test10_join
 * @Describe join()等同于sql里的内连接,关联上的要,关联不上的舍弃
 */
object Test10_join {
  def main(args: Array[String]): Unit = {
    //创建SparkConf并设置App名称
    val conf: SparkConf = new SparkConf().setAppName("SparkCoreTest").setMaster("local[*]")

    //创建SparkContext,该对象是提交SparkApp的入口
    val sc = new SparkContext(conf)

    //使用makeRDD()创建rdd
    val rdd: RDD[(Int, String)] = sc.makeRDD(Array((3, "aa"), (6, "cc"), (2, "bb"), (1, "dd"),(1,"lqs")))
    val rdd1: RDD[(Int, Int)] = sc.makeRDD(Array((1, 4), (2, 5), (4, 6)))

    rdd.join(rdd1).collect().foreach(println)

    //关闭资源
    sc.stop()
  }
}

运行结果如下

(1,(dd,4))
(1,(lqs,4))
(2,(bb,5))

11、 cogroup()类似于sql的全连接,但是在同一个RDD中对key聚合

代码如下

package com.lqs.csdncode.keyvalue

import org.apache.spark.rdd.RDD
import org.apache.spark.{SparkConf, SparkContext}

/**
 * @Author lqs
 * @Date 2022年02月17日 10:48:29
 * @Version 1.0.0
 * @ClassName Test11_cogroup
 * @Describe cogroup()类似于sql的全连接,但是在同一个RDD中对key聚合
 */
object Test11_cogroup {
  def main(args: Array[String]): Unit = {
    //创建SparkConf并设置App名称
    val conf: SparkConf = new SparkConf().setAppName("SparkCoreTest").setMaster("local[*]")

    //创建SparkContext,该对象是提交SparkApp的入口
    val sc = new SparkContext(conf)

    //使用makeRDD()创建rdd
    val rdd: RDD[(Int, String)] = sc.makeRDD(Array((3, "aa"), (6, "cc"), (2, "bb"), (1, "dd"), (1, "lqs")))
    val rdd1: RDD[(Int, Int)] = sc.makeRDD(Array((1, 4), (2, 5), (4, 6)))

    rdd.cogroup(rdd1).collect().foreach(println)

    //关闭资源
    sc.stop()
  }
}

运行结果如下

(1,(CompactBuffer(dd, lqs),CompactBuffer(4)))
(2,(CompactBuffer(bb),CompactBuffer(5)))
(3,(CompactBuffer(aa),CompactBuffer()))
(4,(CompactBuffer(),CompactBuffer(6)))
(6,(CompactBuffer(cc),CompactBuffer()))

action行动算子

1、reduce()聚合

代码如下:

package com.lqs.csdncode.action

import org.apache.spark.{SparkConf, SparkContext}
import org.apache.spark.rdd.RDD

/**
 * @Author lqs
 * @Date 2022年02月17日 11:12:13
 * @Version 1.0.0
 * @ClassName Test01_reduce
 * @Describe reduce()聚合
 *
 *           Action行动算子
 *           行动算子是触发了整个作业的执行。因为转换算子都是懒加载,并不会立即执行。
 *
 */
object Test01_reduce {
  def main(args: Array[String]): Unit = {
    //创建SparkConf并设置App名称
    val conf: SparkConf = new SparkConf().setAppName("SparkCoreTest").setMaster("local[*]")

    //创建SparkContext,该对象是提交SparkApp的入口
    val sc = new SparkContext(conf)

    //使用makeRDD()创建rdd
    val rdd: RDD[Int] = sc.makeRDD(Array(1, 2, 3, 4, 5, 6))

    //聚合数据
    //    val reduceRdd: Int = rdd.reduce((left: Int, right: Int) => left + right)
    val reduceRdd: Int = rdd.reduce(_ + _)
    println(reduceRdd)

    //关闭资源
    sc.stop()
  }
}

运行结果如下:

21

2、collect()以数组的形式返回数据集

代码如下:

package com.lqs.csdncode.action

import org.apache.spark.{SparkConf, SparkContext}
import org.apache.spark.rdd.RDD

/**
 * @Author lqs
 * @Date 2022年02月17日 11:19:50
 * @Version 1.0.0
 * @ClassName Test02_collect
 * @Describe collect()以数组的形式返回数据集
 */
object Test02_collect {
  def main(args: Array[String]): Unit = {
    //创建SparkConf并设置App名称
    val conf: SparkConf = new SparkConf().setAppName("SparkCoreTest").setMaster("local[*]")

    //创建SparkContext,该对象是提交SparkApp的入口
    val sc = new SparkContext(conf)

    //使用makeRDD()创建rdd
    val rdd: RDD[Int] = sc.makeRDD(Array(1, 2, 3, 4, 5, 6))

    //收集数据导Driver
    rdd.collect().foreach(println)

    //关闭资源
    sc.stop()
  }
}

运行结果如下:

1
2
3
4
5
6

3、count()返回RDD中元素个数

代码如下:

package com.lqs.csdncode.action

import org.apache.spark.{SparkConf, SparkContext}
import org.apache.spark.rdd.RDD

/**
 * @Author lqs
 * @Date 2022年02月17日 11:21:56
 * @Version 1.0.0
 * @ClassName Test03_count
 * @Describe count()返回RDD中元素个数
 */
object Test03_count {
  def main(args: Array[String]): Unit = {
    //创建SparkConf并设置App名称
    val conf: SparkConf = new SparkConf().setAppName("SparkCoreTest").setMaster("local[*]")

    //创建SparkContext,该对象是提交SparkApp的入口
    val sc = new SparkContext(conf)

    //使用makeRDD()创建rdd
    val rdd: RDD[Int] = sc.makeRDD(Array(1, 2, 3, 4, 5, 6))

    //返回rdd中元素的个数
    val countResult: Long = rdd.count()
    println(countResult)

    //关闭资源
    sc.stop()
  }
}

运行结果如下:

6

4、first()返回RDD中的第一个元素

代码如下:

package com.lqs.csdncode.action

import org.apache.spark.rdd.RDD
import org.apache.spark.{SparkConf, SparkContext}

/**
 * @Author lqs
 * @Date 2022年02月17日 11:24:18
 * @Version 1.0.0
 * @ClassName Test04_first
 * @Describe first()返回RDD中的第一个元素
 */
object Test04_first {
  def main(args: Array[String]): Unit = {
    //创建SparkConf并设置App名称
    val conf: SparkConf = new SparkConf().setAppName("SparkCoreTest").setMaster("local[*]")

    //创建SparkContext,该对象是提交SparkApp的入口
    val sc = new SparkContext(conf)

    //使用makeRDD()创建rdd
    val rdd: RDD[Int] = sc.makeRDD(Array(1, 2, 3, 4, 5, 6))

    val firstResult: Int = rdd.first()
    println(firstResult)

    //关闭资源
    sc.stop()
  }
}

运行结果如下:

1

5、take()返回由RDD前n个元素组成的数组

代码如下:

package com.lqs.csdncode.action

import org.apache.spark.{SparkConf, SparkContext}
import org.apache.spark.rdd.RDD

/**
 * @Author lqs
 * @Date 2022年02月17日 11:25:54
 * @Version 1.0.0
 * @ClassName Test05_take
 * @Describe take()返回由RDD前n个元素组成的数组
 */
object Test05_take {
  def main(args: Array[String]): Unit = {
    //创建SparkConf并设置App名称
    val conf: SparkConf = new SparkConf().setAppName("SparkCoreTest").setMaster("local[*]")

    //创建SparkContext,该对象是提交SparkApp的入口
    val sc = new SparkContext(conf)

    //使用makeRDD()创建rdd
    val rdd: RDD[Int] = sc.makeRDD(Array(1, 2, 3, 4, 5, 6))

    //返回前3
    val takeResult: Array[Int] = rdd.take(3)
//    takeResult.foreach(println)
    //mkString(" "):把takeResult的结果集按空格分开转换成字符串
    println(takeResult.mkString(" "))

    //关闭资源
    sc.stop()
  }
}

运行结果如下:

1 2 3

6、takeOrdered()返回该RDD排序后前n个元素组成的数组

代码如下:

package com.lqs.csdncode.action

import org.apache.spark.{SparkConf, SparkContext}
import org.apache.spark.rdd.RDD

/**
 * @Author lqs
 * @Date 2022年02月17日 11:30:06
 * @Version 1.0.0
 * @ClassName Test06_takeOrdered
 * @Describe takeOrdered()返回该RDD排序后前n个元素组成的数组
 */
object Test06_takeOrdered {
  def main(args: Array[String]): Unit = {
    //创建SparkConf并设置App名称
    val conf: SparkConf = new SparkConf().setAppName("SparkCoreTest").setMaster("local[*]")

    //创建SparkContext,该对象是提交SparkApp的入口
    val sc = new SparkContext(conf)

    //使用makeRDD()创建rdd
    val rdd: RDD[Int] = sc.makeRDD(Array(4, 1, 3, 6, 2, 5))

    //返回rdd中排序完后的前三个元素
    val takeOrderedResult: Array[Int] = rdd.takeOrdered(3)
    println(takeOrderedResult.mkString(" "))

    //关闭资源
    sc.stop()
  }
}

运行结果如下:

1 2 3

7、aggregate()案例

内部实现(计算)原理
在这里插入图片描述

代码如下:

package com.lqs.csdncode.action

import org.apache.spark.{SparkConf, SparkContext}
import org.apache.spark.rdd.RDD

/**
 * @Author lqs
 * @Date 2022年02月17日 11:33:59
 * @Version 1.0.0
 * @ClassName Test07_aggregate
 * @Describe aggregate行动算子
 *
 *           功能说明:
 *           aggregate函数将每个分区里面的元素通过分区内逻辑和初始值进行聚合,然后用分区间逻辑和初始值(zeroValue)进行操作。
 *
 *           注意事项:
 *           注意:分区间进行逻辑运算时会再次使用一次初始值,这点和aggregateByKey是有区别的。
 *
 */
object Test07_aggregate {
  def main(args: Array[String]): Unit = {
    //创建SparkConf并设置App名称
    val conf: SparkConf = new SparkConf().setAppName("SparkCoreTest").setMaster("local[*]")

    //创建SparkContext,该对象是提交SparkApp的入口
    val sc = new SparkContext(conf)

    //使用makeRDD()创建rdd
    val rdd: RDD[Int] = sc.makeRDD(Array(1, 2, 3, 4, 5, 6), 8)

    val aggergateResult: Int = rdd.aggregate(0)(_ + _, _ + _)
    val aggergateResult1: Int = rdd.aggregate(10)(_ + _, _ + _)

    println(aggergateResult)
    println("################################################")
    println(aggergateResult1)

    //关闭资源
    sc.stop()
  }
}

运行结果如下:

21
################################################
111

8、fold()案例

代码如下:

package com.lqs.csdncode.action

import org.apache.spark.rdd.RDD
import org.apache.spark.{SparkConf, SparkContext}

/**
 * @Author lqs
 * @Date 2022年02月17日 11:43:58
 * @Version 1.0.0
 * @ClassName Test08_fold
 * @Describe fold()案例
 */
object Test08_fold {
  def main(args: Array[String]): Unit = {
    //创建SparkConf并设置App名称
    val conf: SparkConf = new SparkConf().setAppName("SparkCoreTest").setMaster("local[*]")

    //创建SparkContext,该对象是提交SparkApp的入口
    val sc = new SparkContext(conf)

    //使用makeRDD()创建rdd
    val rdd: RDD[Int] = sc.makeRDD(Array(1, 2, 3, 4, 5, 6),8)

    val foldResult: Int = rdd.fold(0)(_ + _)
    println(foldResult)
    println("################################################")
    val foldResult1: Int = rdd.fold(10)(_ + _)
    println(foldResult1)

    //关闭资源
    sc.stop()
  }
}

运行结果如下:

21
################################################
111

9、countByKey()统计每种key的个数

代码如下:

package com.lqs.csdncode.action

import org.apache.spark.{SparkConf, SparkContext}
import org.apache.spark.rdd.RDD

/**
 * @Author lqs
 * @Date 2022年02月17日 11:54:38
 * @Version 1.0.0
 * @ClassName Test09_countByKey
 * @Describe countByKey()统计每种key的个数
 *
 *           功能说明:
 *           统计每种key的个数
 */
object Test09_countByKey {
  def main(args: Array[String]): Unit = {
    //创建SparkConf并设置App名称
    val conf: SparkConf = new SparkConf().setAppName("SparkCoreTest").setMaster("local[*]")

    //创建SparkContext,该对象是提交SparkApp的入口
    val sc = new SparkContext(conf)

    //使用makeRDD()创建rdd
    val rdd: RDD[(Int, String)] = sc.makeRDD(List((1, "a"), (1, "a"), (1, "a"), (2, "b"), (3, "c"), (3, "c")))
    val rdd1: RDD[(String, Int)] = sc.makeRDD(List(("a", 88), ("b", 95), ("a", 91), ("b", 93), ("a", 95), ("b", 98)),2)

    val countByKeyResult: collection.Map[Int, Long] = rdd.countByKey()
    println(countByKeyResult)
    println("################################################")
    
    val countByKeyResult1: collection.Map[String, Long] = rdd1.countByKey()
    println(countByKeyResult1)
    
    //关闭资源
    sc.stop()
  }
}

运行结果如下:

Map(1 -> 3, 2 -> 1, 3 -> 2)
################################################
Map(b -> 3, a -> 3)

10、save相关算子

saveAsTextFile(path)保存成Text文件
	功能说明:
		将数据集的元素以textfile的形式保存到HDFS文件系统或者其他支持的文件系统,对于每个元素,Spark将会调用toString方法,将它装换为文件中的文本
		
saveAsSequenceFile(path) 保存成Sequencefile文件
	功能说明:
		将数据集中的元素以Hadoop Sequencefile的格式保存到指定的目录下,可以使HDFS或者其他Hadoop支持的文件系统。
	注意:
		只有kv类型RDD有该操作,单值的没有
saveAsObjectFile(path) 序列化成对象保存到文件
	功能说明:
		用于将RDD中的元素序列化成对象,存储到文件中。

代码如下:

package com.lqs.csdncode.action

import org.apache.spark.{SparkConf, SparkContext}
import org.apache.spark.rdd.RDD

/**
 * @Author lqs
 * @Date 2022年02月17日 13:46:11
 * @Version 1.0.0
 * @ClassName Test10_saveAndOpen
 * @Describe
 */
object Test10_saveAndRead {
  def main(args: Array[String]): Unit = {
    //创建SparkConf并设置App名称
    val conf: SparkConf = new SparkConf().setAppName("SparkCoreTest").setMaster("local[*]")

    //创建SparkContext,该对象是提交SparkApp的入口
    val sc = new SparkContext(conf)

    //使用makeRDD()创建rdd
    val rdd: RDD[Int] = sc.makeRDD(Array(1, 2, 3, 4, 5, 6), 3)

    //保存成Text文件
    rdd.saveAsTextFile("outputTextFile")

    //序列化对象保存到文件
    rdd.saveAsObjectFile("outputObjectFile")

    //保存成SequenceFile二进制文件
    rdd.map((_, 1)).saveAsSequenceFile("outputSequenceFile")

    //读取Text文件
    val textReadRdd: RDD[String] = sc.textFile("outputTextFile")
    textReadRdd.collect().foreach(println)
    println("#################################")

    //读取ObjectFile
    val objectReadRdd: RDD[Int] = sc.objectFile("outputObjectFile")
    objectReadRdd.collect().foreach(println)
    println("#################################")

    //读取SequenceFile
    val sequenceReadRdd: RDD[(Int, Int)] = sc.sequenceFile[Int, Int]("outputSequenceFile")
    sequenceReadRdd.collect().foreach(println)

    //关闭资源
    sc.stop()
  }
}

运行结果如下:
在这里插入图片描述

1
2
3
4
5
6
#################################
1
2
3
4
5
6
#################################
(1,1)
(2,1)
(3,1)
(4,1)
(5,1)
(6,1)

11、foreach(f)遍历RDD中每一个元素

代码如下:

package com.lqs.csdncode.action

import org.apache.spark.{SparkConf, SparkContext}
import org.apache.spark.rdd.RDD

/**
 * @Author lqs
 * @Date 2022年02月17日 14:00:07
 * @Version 1.0.0
 * @ClassName Text11_foreach
 * @Describe foreach(f)遍历RDD中每一个元素
 */
object Text11_foreach {
  def main(args: Array[String]): Unit = {
    //创建SparkConf并设置App名称
    val conf: SparkConf = new SparkConf().setAppName("SparkCoreTest").setMaster("local[*]")

    //创建SparkContext,该对象是提交SparkApp的入口
    val sc = new SparkContext(conf)

    //使用makeRDD()创建rdd
    val rdd: RDD[Int] = sc.makeRDD(Array(1, 2, 3, 4, 5, 6),2)

    //收集后打印
    rdd.collect().foreach(println)

    println("################################################")

    //分布式打印
    rdd.foreach(println)

    //关闭资源
    sc.stop()
  }
}

运行结果如下:

1
2
3
4
5
6
################################################
4
1
5
2
6
3

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小雏菊的成长

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值