从零开始学习Saprk - 32种Saprk算子详解(完整版)

一.Transform类型算子

1.1 Value 类型

1.1.1 map 算子

介绍 :

返回一个新的RDD,该RDD由每一个输入元素经过func函数转换后组成 ,也就是说RDD中每个元素都会执行一次这个方法

代码 :

		// 创建SparkConf 设置本地运行模式
		val conf = new SparkConf()
    		.setMaster("local[1]")
    		.setAppName("MapOperator")
		// 创建SparkContext
		val sc = new SparkContext(conf)
		sc.setLogLevel("ERROR")

		// 创建数据
		val rdd = sc.parallelize(List("李白", "韩信", "张飞")).cache()
		// 使用 Map 算子
		val result = rdd.map((x) => (x,1))
		// 打印结果
		result.foreach((x) => println(x.toString()))

		// 关闭SparkContext
		sc.stop()
1.1.2 mapParatition 算子

介绍 :

类似于 map,但独立地在 RDD 的每一个分片上运行,因此在类型为 T 的 RDD 上
运行时,func 的函数类型必须是 Iterator[T] => Iterator[U]。假设有 N 个元素,有 M 个分区,
那么 map 的函数的将被调用 N 次,而 mapPartitions 被调用 M 次,一个函数一次处理所有分区。

代码 :

		// 创建SparkConf 设置本地运行模式
		val conf = new SparkConf()
			.setMaster("local[1]")
			.setAppName("MapPartitionsOperator")
		// 创建SparkContext
		val sc = new SparkContext(conf)
		sc.setLogLevel("ERROR")

		/**
		 * 类似于 map,但独立地在 RDD 的每一个分片上运行 , 而 Map 是在
		 * 每一个元素上运行一次
		 */

		// 创建数据
		val rdd = sc.parallelize(List("李白", "韩信", "张飞")).cache()
		// 使用 MapParatitions 算子
		// 补充 : { 一般是 写代码块的时候用 ( 是 单行代码可以直接用

		def fun(x : Iterator[String]): Iterator[Tuple2[String, Int]] ={
			// 创建Tuple类型的集合 用于存储数据
			// List 需要创建成 var 类型的因为拼接时需要指向新的List的对象
			var list = List[Tuple2[String, Int]]()

			while (x.hasNext) {
				// x
				var elem = x.next()
				// 将数据存入List中 再使用拼接集合的方式将数据添加到
				list = list.:::(List(new Tuple2[String, Int](elem, 1)))
				// 备注 : 这里 ::: 和 :: 的区别是 ::: 的参数是 List , 而 :: 的参数是元素
			}
			list.iterator
		}

		// 可以直接使用匿名函数或者直接定义函数传入有
		// rdd.mapPartitions(fun)

		val result = rdd.mapPartitions { x =>

			// 创建Tuple类型的集合 用于存储数据
			// List 需要创建成 var 类型的因为拼接时需要指向新的List的对象
			var list = List[Tuple2[String, Int]]()

			while (x.hasNext) {
				// x
				var elem = x.next()
				// 将数据存入List中 再使用拼接集合的方式将数据添加到
				list = list.:::(List(new Tuple2[String, Int](elem, 1)))
				// 备注 : 这里 ::: 和 :: 的区别是 ::: 的参数是 List , 而 :: 的参数是元素
			}
			list.iterator
		}

		// 遍历结果并打印
		result.foreach(println(_))

		// 关闭SparkContext
		sc.stop()
1.1.3 mapPartitionsWithIndex 算子
  1. 作用:mapPartitionsWithIndex(func) 类似于 mapPartitions,但 func 带有一个整数参数表示分片的索引值,相当于带索引的
  2. 因此在类型为 T 的 RDD 上运行时,func 的函数类型必须是(Int, Interator[T]) => Iterator[U];
  3. 需求:创建一个 RDD,使每个元素跟所在分区形成一个元组组成一个新的 RDD

代码 :

		// 带索引的 MapPartitions
		// 创建SparkConf 设置本地运行模式
		val conf = new SparkConf()
			.setMaster("local[1]")
			.setAppName("MapPartitionsWithIndexOperator")
		// 创建SparkContext
		val sc = new SparkContext(conf)
		sc.setLogLevel("ERROR")

		// 创建数据
		val rdd = sc.parallelize(List("李白", "韩信", "张飞")).cache()

		def fun(index: Int, x: Iterator[String]): Iterator[Tuple2[Int, String]] = {

			// List 需要创建成 var 类型的因为拼接时需要指向新的List的对象
			var list = List[Tuple2[Int, String]]()

			while(x.hasNext){
				// 获取迭代器中的元素
				var elem = x.next()
				// 注意 : .::() 的参数是元素, 而 .:::() 的参数是 List
				list = list.::(new Tuple2[Int, String](index, elem))
			}

			list.iterator
		}

		val result = rdd.mapPartitionsWithIndex(fun)

		// 遍历结果并打印
		result.foreach(println(_))
		// 关闭SparkContext
		sc.stop()
1.1.4 flatMap 算子

类似于 map,但是每一个输入元素可以被映射为 0 或多个输出元素(所以 func 应
该返回一个序列,而不是单一元素)

比如 : 如果正常情况返回的是 :

List(1,2,3)

List(1,2,3,4)

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

FlatMap 会将数据压平, 都放到一个List里面

123 1234 12345

代码 :

// 创建 SparkContext
		val conf = new SparkConf()
			.setAppName("FlatMapOperator")
			.setMaster("local[1]")
		val sc = new SparkContext(conf)
		// 设置SparkContext的打印日志的级别
		sc.setLogLevel("WARN")

		/**
		  * 创建RDD :
		  * 1. 使用 paralize : seq => rdd 的本地集合 : List ... , numSlices : 分区数
		  * 2. 适用 makeRDD
		  */

		val rdd = sc.makeRDD(1 to 5)
		val flatMapResult = rdd.flatMap(1 to _)
		val mapResult = rdd.map(1 to _)

		// Map 和 Flat Map 的区别
		mapResult.foreach(println(_))
		flatMapResult.foreach(println(_))

		// 关闭 SparkContext
		sc.stop()
1.1.5 map 和 mapParatition的区别
  1. map():每次处理一条数据。
  2. mapPartition():每次处理一个分区的数据,这个分区的数据处理完后,原 RDD 中分区的
    数据才能释放,可能导致 OOM。
  3. 开发指导:当内存空间较大的时候建议使用 mapPartition(),以提高处理效率。
1.1.6 glom 算子
  1. 作用:将每一个分区形成一个数组,形成新的 RDD 类型时 RDD[Array[T]]
  2. 需求:创建一个 4 个分区的 RDD,并将每个分区的数据放到一个数组

代码 :

// 创建 SparkContext
		val conf = new SparkConf()
			.setAppName("Spark APP")
			.setMaster("local[1]")
		val sc = new SparkContext(conf)
		// 设置SparkContext的打印日志的级别
		sc.setLogLevel("WARN")

		//1. 作用:将每一个分区形成一个数组,形成新的 RDD 类型时 RDD[Array[T]]
		//2. 需求:创建一个 4 个分区的 RDD,并将每个分区的数据放到一个数组

		// 生成rdd数据, 设置分区为 4
		val rdd = sc.parallelize(1 to 10, 4)
		// 注意返回值
		val result : RDD[Array[Int]] = rdd.glom()

		result.foreach(arr => {
			// arr 是 Array 类型
			for (i <- 0 until arr.length) {
				// 打印结果
				println(arr(i))
			}
			println("==========================================")
		})

		// 关闭SparkContext
		sc.stop()
1.1.7 groupBy 算子
  1. 作用:分组,按照传入函数的返回值进行分组。将相同的 key 对应的值放入一个迭代器。
  2. 需求:创建一个 RDD,按照元素模以 2 的值进行分组
  3. 补充: 这个算子的效率并不高, 不推荐使用, 详情可以查看源码中对应方法的Note部分

代码 :

// 创建 SparkContext
		val conf = new SparkConf()
			.setAppName("Spark APP")
			.setMaster("local[1]")
		val sc = new SparkContext(conf)
		// 设置SparkContext的打印日志的级别
		sc.setLogLevel("WARN")

		// 创建数据
		val rdd = sc.parallelize(1 to 10)

		val result : RDD[(String, Iterable[Int])]= rdd.groupBy(x => {

			var key = ""

			x match {
				case _ if (x < 3) => {
					key = "small"
				}
				case _ if (x > 3 && x < 5) => {
					key = "big"
				}
				case _ if (x > 5) => {
					key = "very big"
				}
				case _ => {
					key = "void"
				}
			}
			key
		})

		// 遍历结果
		result.foreach(x => {
			println("key : " + x._1 + " \t" + x._2)
		})

		sc.stop()
1.1.8 filter 算子
  1. 作用:过滤。返回一个新的 RDD,该 RDD 由经过 func 函数计算后返回值为 true 的输入
    元素组成。
  2. 需求:创建一个 RDD(由字符串组成),过滤出一个新 RDD(包含”xiao”子串)

代码 :

// 创建 SparkContext
		val conf = new SparkConf()
			.setAppName("Spark APP")
			.setMaster("local[1]")
		val sc = new SparkContext(conf)
		// 设置SparkContext的打印日志的级别
		sc.setLogLevel("WARN")

		val rdd = sc.parallelize(1 to 10)

		// 过滤。返回一个新的 RDD,该 RDD 由经过 func 函数计算后返回值为 true 的输入元素组成。
		val result : RDD[Int] = rdd.filter(x => {
			if (x % 2 == 0){
				true
			}
			false
		})

		// 打印结果
		result.foreach(println(_))
		sc.stop()
1.1.9 sample 算子
  1. 作用:以指定的随机种子随机抽样出数量为 fraction 的数据,

fraction 大小在 [0,1] 代表抽出百分之 多少的数据 比如 fraction = 0.3 表示抽出 30% 的数据

withReplacement 表示是抽出的数据是否放回,true 为有放回的抽样,false 为无放回的抽样,seed 用于指定随机数生成器种子。

  1. 需求:创建一个 RDD(1-10),从中选择放回和不放回抽样

代码 :

	// 创建 SparkContext
		val conf = new SparkConf()
			.setAppName("Spark APP")
			.setMaster("local[1]")
		val sc = new SparkContext(conf)
		// 设置SparkContext的打印日志的级别
		sc.setLogLevel("WARN")

		// 创建数据
		val rdd = sc.parallelize(1 to 200)
		// 抽样
		/**
		 * 作用:以指定的随机种子随机抽样出数量为 fraction * 总数据量的的数据,
		 * fraction 大小在 [0,1] 代表抽出百分之 多少的数据 比如 fraction = 0.3 表示抽出 30% 的数据
		 * withReplacement 表示是抽出的数据是否放回,true 为有放回的抽样,false 为无放回的抽样,seed 用于指定随机数生成
		 * 器种子。
		 */
		val result = rdd.sample(true, 0.2, 1234L)
		// 遍历
		result.foreach(println(_))
		// 关闭SparkContext
		sc.stop()
1.1.10 distinct 算子
  1. 作用:对源 RDD 进行去重后返回一个新的 RDD。默认情况下,只有 8 个并行任务来操
    作,但是可以传入一个可选的 numTasks 参数改变它。
  2. 需求:创建一个 RDD,使用 distinct() 对其去重

代码 :

// 创建 SparkContext
		val conf = new SparkConf()
			.setAppName("Spark APP")
			.setMaster("local[1]")
		val sc = new SparkContext(conf)
		// 设置SparkContext的打印日志的级别
		sc.setLogLevel("WARN")

		// 创建数据
		val rdd = sc.parallelize(List("张", "韩", "李", "韩", "王", "王"))
		// 使用去重
		val result = rdd.distinct()
		// 打印结果
		result.foreach(println(_))
		sc.stop()
1.1.11 coalesce 算子
  1. 作用:缩减分区数,用于大数据集过滤后,提高小数据集的执行效率。

如果数据较少的情况下, 分区数太多其实并不好,

  1. 需求:创建一个 4 个分区的 RDD,对其缩减分区

代码 :

		// 创建 SparkContext
		val conf = new SparkConf()
			.setAppName("Spark APP")
			.setMaster("local[1]")
		val sc = new SparkContext(conf)
		// 设置SparkContext的打印日志的级别
		sc.setLogLevel("WARN")

		// 产生数据
		val rdd = sc.parallelize(1 to 10, 4)

		// 缩小分区
		// numPartitions 是重新的分区数据 , shuffle true 表示重新对数据进行shuffle
		// 默认为 false
		val result = rdd.coalesce(2, true)
		// 重新分区后的分区数
		val numPartitions = result.partitions.size
		println(numPartitions)

		sc.stop()
1.1.12 repartition 算子
  1. 作用:根据分区数,重新通过网络随机洗牌所有数据。使用 coalesce 算子也可以达到相同的效果

shuffle 后可以让数据分布更均匀

  1. 需求:创建一个 4 个分区的 RDD,对其重新分区

代码 :

def printPartition(rdd : RDD[Int]): Unit ={

		rdd.foreachPartition(f => {
			// 遍历 Iterator 类型的数据的方法
			while(f.hasNext){
				var element = f.next()
				print(element)
			}
			println()
		})
	}

	def main(args: Array[String]): Unit = {

		// 创建 SparkContext
		val conf = new SparkConf()
			.setAppName("Spark APP")
			.setMaster("local[1]")
		val sc = new SparkContext(conf)
		// 设置SparkContext的打印日志的级别
		sc.setLogLevel("WARN")

		// 创建数据
		val rdd = sc.parallelize(1 to 15, 4)
		// 打印各个分区的数据
		printPartition(rdd)

		// 重新分区 并shuffle 数据
		// shuffle 后可以让数据分布更均匀
		val result = rdd.repartition(3)
		println("重新分区 并shuffle 数据.....")
		printPartition(result)

		sc.stop()
	}
1.1.13 coalesce 和 repartition 的区别
  1. coalesce 重新分区,可以选择是否进行 shuffle 过程。由参数 shuffle: Boolean = false/true 决定。
  2. repartition 实际上是调用的 coalesce,默认是进行 shuffle 的。源码如下:

代码 :

def repartition(numPartitions: Int)(implicit ord: Ordering[T] = null): RDD[T] = withScope {
coalesce(numPartitions, shuffle = true)
}
1.1.14 sortBy 算子
  1. 作用; sortBy(func,[ascending], [numTasks])使用 func 先对数据进行处理,按照处理后的数据比较结果排序,默认为正序。注意 : 最终返回的结果是排序后的原数据!
  2. 需求:创建一个 RDD,按照不同的规则进行排序

代码 :

		// 创建 SparkContext
		val conf = new SparkConf()
			.setAppName("Spark APP")
			.setMaster("local[1]")
		val sc = new SparkContext(conf)
		// 设置SparkContext的打印日志的级别
		sc.setLogLevel("WARN")

		val rdd = sc.parallelize(List(1,4,2,5,8,3))
		// 使用 函数先对数据进行处理, 然后在对处理过的数据进行排序
		// 注意 : 最终返回的仍然是 原数据
		// 比如 : 如果是 f % 2 , 4 % 2 => 0, 1 % 2 => 1
		// 所以排名是 1 在 4前面 => 1,4
		val result = rdd.sortBy(f => {
			f % 2
		})
		// 遍历结构
		result.foreach(println(_))
		sc.stop()
1.1.15 pipe 算子
  1. 作用:管道,针对每个分区,都执行一个 shell 脚本,返回输出的 RDD。
    注意:脚本需要放在 Worker 节点可以访问到的位置
  2. 需求:编写一个脚本,使用管道将脚本作用于 RDD 上。
  3. 注意: 这个在windows上暂时是无法运行的, 因为需要执行shell脚本

代码 :

		// 创建 SparkContext
		val conf = new SparkConf()
			.setAppName("Spark APP")
			.setMaster("local[1]")
		val sc = new SparkContext(conf)
		// 设置SparkContext的打印日志的级别
		sc.setLogLevel("WARN")

		val rdd = sc.parallelize(List("韩信", "白龙", "青帝"), 1)
		val result = rdd.pipe("pipe.sh")
		// 打印结果
		result.foreach(print(_))
		sc.stop()

1.2 双 Value 类型交互

1.2.1 union 算子
  1. 作用:对源 RDD 和参数 RDD 求并集后返回一个新的 RDD
  2. 需求:创建两个 RDD,求并集

代码 :

		// 创建 SparkContext
		val conf = new SparkConf()
			.setAppName("Spark APP")
			.setMaster("local[1]")
		val sc = new SparkContext(conf)
		// 设置SparkContext的打印日志的级别
		sc.setLogLevel("WARN")
		// 创建数据
		val rdd = sc.parallelize(1 to 5)
		val otherRdd = sc.parallelize(3 to 6)
		// 求两个RDD 的并集
		val result = rdd.union(otherRdd)
		// 打印结果
		result.foreach(println(_))
		sc.stop()
1.2.2 subtract 算子
  1. 作用:计算差的一种函数,去除两个 RDD 中相同的元素,不同的数据将保留下来

比如 : rdd : 1,2,3,4,5,6 otherRdd : 4,5,6,7,8

它会把 rdd 和 otherRdd 中都有的数据从 rdd 中清除然后把rdd剩余的数据返回来

结果是 : result : 1,2,3

  1. 需求:创建两个 RDD,求第一个 RDD 与第二个 RDD 的差集

代码 :


		// 创建 SparkContext
		val conf = new SparkConf()
			.setAppName("Spark APP")
			.setMaster("local[1]")
		val sc = new SparkContext(conf)
		// 设置SparkContext的打印日志的级别
		sc.setLogLevel("WARN")

		// 创建数据
		val rdd = sc.parallelize(1 to 6)
		val otherRdd = sc.parallelize(4 to 8)

		// 计算两个rdd 的差集, 去除两个rdd的相同的数据, 保留不同的数据
		// 特别注意 !!! 
		// 比如 : rdd : 1,2,3,4,5,6  otherRdd : 4,5,6,7,8 
		// 它会把 rdd 和 otherRdd 中都有的数据从 rdd 中清除然后把rdd剩余的数据返回来
		val result = rdd.subtract(otherRdd)
		// 打印结果
		result.foreach(println(_))
		sc.stop()
1.2.3 intersection 算子
  1. 作用:对源 RDD 和参数 RDD 求交集后返回一个新的 RDD
  2. 需求:创建两个 RDD,求两个 RDD 的交集

代码 :

		// 创建 SparkContext
		val conf = new SparkConf()
			.setAppName("Spark APP")
			.setMaster("local[1]")
		val sc = new SparkContext(conf)
		// 设置SparkContext的打印日志的级别
		sc.setLogLevel("WARN")

		// 创建数据
		val rdd = sc.parallelize(1 to 6)
		val otherRdd = sc.parallelize(4 to 8)
		
		// 对源 RDD 和参数 RDD 求交集后返回一个新的 RDD
		val result = rdd.intersection(otherRdd)
		// 打印结果
		result.foreach(println(_))
		sc.stop()
1.2.4 cartesian 算子
  1. 作用:笛卡尔积(尽量避免使用
  2. 需求:创建两个 RDD,计算两个 RDD 的笛卡尔积

代码 :

		// 创建 SparkContext
		val conf = new SparkConf()
			.setAppName("Spark APP")
			.setMaster("local[1]")
		val sc = new SparkContext(conf)
		// 设置SparkContext的打印日志的级别
		sc.setLogLevel("WARN")

		// 创建数据
		val rdd = sc.parallelize(1 to 6)
		val otherRdd = sc.parallelize(4 to 8)

		// 计算笛卡尔积
		// 谨慎使用, 因为计算结果的数量级会很大
		// 比如 : 两个10万数据的rdd计算笛卡尔 结果是 10万 * 10万 => 100亿
		val result = rdd.cartesian(otherRdd)
		// 打印结果
		result.foreach(println(_))
		sc.stop()
1.2.5 zip 算子
  1. 作用:将两个 RDD 组合成 Key/Value 形式的 RDD,这里默认两个 RDD 的 partition 数量以
    及元素数量都相同,否则会抛出异常。
  2. 需求:创建两个 RDD,并将两个 RDD 组合到一起形成一个(k,v)RDD

代码 :

		// 创建 SparkContext
		val conf = new SparkConf()
			.setAppName("Spark APP")
			.setMaster("local[1]")
		val sc = new SparkContext(conf)
		// 设置SparkContext的打印日志的级别
		sc.setLogLevel("WARN")

		// 创建数据
		val rdd = sc.parallelize(List(1,2,3), 1)
		val otherRdd = sc.parallelize(List("A", "B", "C"), 1)

		//将两个 RDD 组合成 Key/Value 形式的 RDD,这里默认两个 RDD 的 partition 数量以
		//及元素数量都相同,否则会抛出异常 !
		val result = rdd.zip(otherRdd)
		result.foreach(println(_))
		sc.stop()

1.3 Key-Value 类型

1.3.1 partitionBy 算子
  1. 作用:对 pairRDD 进行分区操作,如果原有的 partionRDD 和现有的 partionRDD 是一致
    的话就不进行分区, 否则会生成 ShuffleRDD,即会产生 shuffle 过程。
  2. 需求:创建一个 4 个分区的 RDD,对其重新分区

代码 :

		// 创建 SparkContext
		val conf = new SparkConf()
			.setAppName("Spark APP")
			.setMaster("local[1]")
		val sc = new SparkContext(conf)
		// 设置SparkContext的打印日志的级别
		sc.setLogLevel("WARN")

		// 创建数据
		val rdd = sc.parallelize(Array("AA","BB","CC"), 4).map(x => (x,1))

		//对 pairRDD 进行分区操作,如果原有的 partionRDD 和现有的 partionRDD 是一致
		//的话就不进行分区, 否则会生成 ShuffleRDD,即会产生 shuffle 过程。
		// 查看分区数
		println("分区数 : ====> " + rdd.partitions.size)
		// 对rdd 重新分区
		val result = rdd.partitionBy(new HashPartitioner(2))
		println("重新分区数 : ====> " + result.partitions.size)
		sc.stop()
1.3.2 groupByKey 算子
  1. 作用:groupByKey 也是对每个 key 进行操作,但只生成一个 sequence。
  2. 需求:创建一个 pairRDD,将相同 key 对应值聚合到一个 sequence 中,并计算相同 key
    对应值的相加结果。

代码 :

		// 创建 SparkContext
		val conf = new SparkConf()
			.setAppName("Spark APP")
			.setMaster("local[1]")
		val sc = new SparkContext(conf)
		// 设置SparkContext的打印日志的级别
		sc.setLogLevel("WARN")

		// 创建数据
		val pairRdd = sc.parallelize(List("hello", "word", "word", "hello"))
			.map(x => (x,1))
		// 将key相同的数据聚合在一起
		val result = pairRdd.groupByKey(2).map(x => (x._1, x._2.sum))
		// 打印数据
		result.foreach(println(_))
		sc.stop()
1.3.3 reduceByKey 算子
  1. 在一个(K,V)的 RDD 上调用,返回一个(K,V)的 RDD,使用指定的 reduce 函数,将相同
    key 的值聚合到一起,reduce 任务的个数可以通过第二个可选的参数来设置。
  2. 需求:创建一个 pairRDD,计算相同 key 对应值的相加结果

代码 :

// 创建 SparkContext
		val conf = new SparkConf()
			.setAppName("Spark APP")
			.setMaster("local[1]")
		val sc = new SparkContext(conf)
		// 设置SparkContext的打印日志的级别
		sc.setLogLevel("WARN")

		// 创建数据
		val pairRdd = sc.parallelize(List("hello", "word", "word", "hello"))
			.map(x => (x,1))
		// 注意 : 这两个传入的都是 value的值
		val result = pairRdd.reduceByKey((v1,v2) => {
			v1 + v2
		})
		// 打印结果
		result.foreach(println(_))
		sc.stop()
1.3.4 reduceByKey和groupByKey 的区别
  1. reduceByKey:按照 key 进行聚合,在 shuffle 之前有 combine(预聚合)操作,返回结果
    是 RDD[k,v].
  2. groupByKey:按照 key 进行分组,直接进行 shuffle。
  3. 开发指导:reduceByKey 比 groupByKey,建议使用。但是需要注意是否会影响业务逻辑
1.3.5 aggregateByKey 算子

参数:(zeroValue:U,[partitioner: Partitioner]) (seqOp: (U, V) => U,combOp: (U, U) => U)

  1. 作用:在 kv 对的 RDD 中,,按 key 将 value 进行分组合并,合并时,将每个 value 和初
    始值作为 seq 函数的参数,进行计算,返回的结果作为一个新的 kv 对,然后再将结果按照
    key 进行合并,最后将每个分组的 value 传递给 combine 函数进行计算(先将前两个 value
    进行计算,将返回结果和下一个 value 传给 combine 函数,以此类推),将 key 与计算结果作
    为一个新的 kv 对输出。

  2. 参数描述:
    (1)zeroValue:给每一个分区中的每一个 key 一个初始值;
    (2)seqOp:函数用于在每一个分区中用初始值逐步迭代 value;
    (3)combOp:函数用于合并每个分区中的结果。

  3. 需求:创建一个 pairRDD,取出每个分区相同 key 对应值的最大值,然后相加

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-h7oDiLhu-1573987055209)(image/1573972672664.png)]

代码 :

		// 创建 SparkContext
		val conf = new SparkConf()
			.setAppName("Spark APP")
			.setMaster("local[1]")
		val sc = new SparkContext(conf)
		// 设置SparkContext的打印日志的级别
		sc.setLogLevel("WARN")

		val rdd = sc.parallelize(List("A","B","C"),2).map((x) => (x,1))
		// 可以直接转换pairRdd
		val pairRdd = sc.parallelize(List(("a",3),("a",2),("c",4),("b",3),("c",6),("c",8)),2)
		// 取出每个分区相同key对应的最大值, 然后相加
		//(1)zeroValue:给每一个分区中的每一个 key 一个初始值;
		//(2)seqOp:函数用于在每一个分区中用初始值逐步迭代 value;
		//(3)combOp:函数用于合并每个分区中的结果。
		// 注意 : 整个过程中 ! pairRdd 的key是不参与运算的
		val result = pairRdd.aggregateByKey(0)((k, v) => {
			// k是 zeroValue, v就是 rdd的value值
			math.max(k,v)
		}, (u1,u2) => {
			// 用于合并value的值
			u1 + u2
		})
		// 打印结果
		result.foreach(println(_))
		sc.stop()
1.3.6 foldByKey 算子

参数:(zeroValue: V)(func: (V, V) => V): RDD[(K, V)]

  1. 作用:aggregateByKey 的简化操作,seqop 和 combop 相同
  2. 需求:创建一个 pairRDD,计算相同 key 对应值的相加结果

代码 :

// 创建 SparkContext
		val conf = new SparkConf()
			.setAppName("Spark APP")
			.setMaster("local[1]")
		val sc = new SparkContext(conf)
		// 设置SparkContext的打印日志的级别
		sc.setLogLevel("WARN")

		// 可以直接转换pairRdd
		val pairRdd = sc.parallelize(List(("a",3),("a",2),("c",4),("b",3),("c",6),("c",8)),2)
		//1. 作用:aggregateByKey 的简化操作,seqop 和 combop 相同
		//2. 需求:创建一个 pairRDD,计算相同 key 对应值的相加结果
		val result = pairRdd.foldByKey(0)((v1,v2) => {
			v1 + v2
		})
		// 打印结果
		result.foreach(println(_))
		sc.stop()
1.3.7 combineByKey 算子

参数:(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() 方法将各个分区的结果进行合并。

  3. 需求:创建一个 pairRDD,根据 key 计算每种 key 的均值。(先计算每个 key 出现的次数
    以及可以对应值的总和,再相除得到结果

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-S0BwIGcf-1573987055211)(image/1573976596770.png)]

代码 :

// 创建 SparkContext
		val conf = new SparkConf()
			.setAppName("Spark APP")
			.setMaster("local[1]")
		val sc = new SparkContext(conf)
		// 设置SparkContext的打印日志的级别
		sc.setLogLevel("WARN")

		// 创建数据
		val rdd = sc.parallelize(
			Array(("a", 88), ("b", 95), ("a", 91), ("b", 93), ("a", 95), ("b", 98)),2)

		val result = rdd.combineByKey(
			// createCombiner
			// ("a",88) ("a", 91) 每种 key("a") 相同的只会生成一个 (88,1) 91,95 会在merge阶段合并
			(x) => {(x,1)},
			// mergeValue 这个和阶段是合并 key(key是指rdd的key 比如"a" / "b")相同的数据,
			// 比如 a 的数据经过 createCombiner 得到的结果是 : (88,1) 91,95
			// 开始 merge acc : (88,1) v: 91, => (179 , 2)
			(acc:(Int,Int), v) => {(acc._1 + v, acc._2 + 1)},
			// 这个阶段是各个分区中相同key的数据进行合并
			// 比如 : 分区1的是 (179,2) 分区2的是 (95,1) => 合并以后就是 (274,3) 
			// (274,3) 274是
			(acc1 : (Int,Int), acc2 : (Int, Int)) =>{(acc1._1 + acc2._1, acc1._2 + acc2._2)}
		)

		result.foreach(println(_))
		sc.stop()
1.3.8 sortByKey 算子
  1. 作用:在一个(K,V)的 RDD 上调用,K 必须实现 Ordered 接口,返回一个按照 key 进行排
    序的(K,V)的 RDD
  2. 需求:创建一个 pairRDD,按照 key 的正序和倒序进行排序

代码 :

		// 创建 SparkContext
		val conf = new SparkConf()
			.setAppName("Spark APP")
			.setMaster("local[1]")
		val sc = new SparkContext(conf)
		// 设置SparkContext的打印日志的级别
		sc.setLogLevel("WARN")

		// 创建数据
		val rdd = sc.parallelize(Array((3,"aa"),(6,"cc"),(2,"bb"),(1,"dd")))
		// 根据key排序 true 为正序, false 为倒序
		val result = rdd.sortByKey(true)
		result.foreach(println(_))
		sc.stop()
1.3.9 mapValues 算子
  1. 针对于(K,V)形式的类型只对 V 进行操作
  2. 需求:创建一个 pairRDD,并将 value 添加字符串"|||"

代码 :

		// 创建 SparkContext
		val conf = new SparkConf()
			.setAppName("Spark APP")
			.setMaster("local[1]")
		val sc = new SparkContext(conf)
		// 设置SparkContext的打印日志的级别
		sc.setLogLevel("WARN")
		// 创建数据
		val rdd = sc.parallelize(Array((1,"a"),(1,"d"),(2,"b"),(3,"c")))
		// 只针对value进行操作
		val result = rdd.mapValues(v => {
			v + "|||"
		})
		// 打印结果
		result.foreach(println(_))
		sc.stop()
1.3.10 join 算子
  1. 作用:在类型为(K,V)和(K,W)的 RDD 上调用,返回一个相同 key 对应的所有元素对在一
    起的(K,(V,W))的 RDD
  2. 需求:创建两个 pairRDD,并将 key 相同的数据聚合到一个元组。

代码 :

		// 创建 SparkContext
		val conf = new SparkConf()
			.setAppName("Spark APP")
			.setMaster("local[1]")
		val sc = new SparkContext(conf)
		// 设置SparkContext的打印日志的级别
		sc.setLogLevel("WARN")

		// 创建数据
		val rdd = sc.parallelize(Array((1,"a"), (2,"b"), (3,"c")))
		val otherRdd = sc.parallelize(Array((1,"A"), (2,"B"), (3,"C")))

		// 在类型为(K,V)和(K,W)的 RDD 上调用,返回一个相同 key 对应的所有元素对在一
		// 起的(K,(V,W))的 RDD
		val result = rdd.join(otherRdd)
		result.foreach(println(_))
		sc.stop()
1.3.11 cogroup 算子
  1. 作用:在类型为(K,V)和(K,W)的 RDD 上调用,返回一个(K,(Iterable,Iterable))类
    型的 RDD
  2. 需求:创建两个 pairRDD,并将 key 相同的数据聚合到一个迭代器。

代码 :

		// 创建 SparkContext
		val conf = new SparkConf()
			.setAppName("Spark APP")
			.setMaster("local[1]")
		val sc = new SparkContext(conf)
		// 设置SparkContext的打印日志的级别
		sc.setLogLevel("WARN")

		//创建数据
		val rdd = sc.parallelize(Array((1,"a"), (2,"b"), (3,"c")))
		val otherRdd = sc.parallelize(Array((1,"A"), (2,"B"), (3,"C")))

		// 在类型为(K,V)和(K,W)的 RDD 上调用,返回一个(K,(Iterable<V>,Iterable<W>))类
		//型的 RDD
		val result = rdd.cogroup(otherRdd)
		result.foreach(value => {

			val key = value._1
			val v1 : Iterable[String] = value._2._1
			val v2 : Iterable[String] = value._2._2

			print(key + " ")
			print(v1 + " ")
			print(v2 + " ")
			println()
		})
		sc.stop()
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

兀坐晴窗独饮茶

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

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

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

打赏作者

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

抵扣说明:

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

余额充值