简介:由于本篇文章写的内容多,且杂,加上本人在写的过程中还加入了大量解释,为此本篇文章会设置成粉丝可见,请谅解!
本篇文章主要包含了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类型
- 双Value类型交互
- Key-Value类型
- Action行动算子
- 各种算子的相关代码如下
- Value类型
- 双Value类型交互
- Key-Value类型
- action行动算子
value类型
序号 | 名称 | 函数签名 | 功能说明 |
---|---|---|---|
01 | map()映射,以行为单位执行 | def map[U:ClassTag](f:T=>U):RDD[U] | 参数f是一个函数,它可以接收一个参数。当某个RDD执行map方法时,会遍历RDD中的每一个数据项,并依次应用f函数,从而产生一个新的RDD。即这个新的RD中的每一个元素都是原来RDD中每一个元素依次应用f函数而得到的。 |
02 | mapPartitions()以分区为单位执行Map | def mapPartitions[U:ClassTag](f:Iterator[T]=>Itertor[U], //把f函数每一个分区的数据分别放入到迭代器中,进行批处理 preserversPartitioning:Boolean=false):RDD[U] //preserversPartitioning:是否保留上游RDD的分区信息,默认false | Map是一次处理一个元素,mapPartitions是一次处理一个分区数据。 |
03 | mapPartitionsWithIndex()带分区号 | def mapPartitionsWithIndex[U: ClassTag]( f: (Int, Iterator[T]) => Iterator[U], // Int表示分区编号 preservesPartitioning: Boolean = false): RDD[U] | 类似于mapPartitions,比mapPartitions多一个整数参数表示分区号。 |
04 | flatMap()扁平化 | def flatMap[U: ClassTag](f: T => TraversableOnce[U]): RDD[U] | 与map操作类似,将RDD中的每一个元素通过应用f函数依次转换为新的元素,并封装到RDD中。 区别:在flatMap操作中,f函数的返回值是一个集合,并且会将每一个该集合中的元素拆分出来放到新的RDD中。 |
05 | glom()分区转换数组 | def glom(): RDD[Array[T]] | 该操作将RDD中每一个分区变成一个数组,并放置在新的RDD中,数组中元素的类型与原分区中元素类型一致。 |
06 | groupBy()分组 | def groupBy[K](f: T => K)(implicit kt: ClassTag[K]): RDD[(K, Iterable[T])] | 分组,按照传入函数的返回值进行分组。将相同的key对应的值放入一个迭代器。 |
07 | filter()过滤 | def filter(f: T => Boolean): RDD[T] | 接收一个返回值为布尔类型的函数作为参数。当某个RDD调用filter方法时,会对该RDD中每一个元素应用f函数,如果返回值类型为true,则该元素会被添加到新的RDD中。 |
08 | sample()采样 | def sample(withReplacement: Boolean, fraction: Double,seed: Long = Utils.random.nextLong): RDD[T]// withReplacement: true为有放回的抽样,false为无放回的抽样;// fraction表示:以指定的随机种子随机抽样出数量为fraction的数据;// seed表示:指定随机数生成器种子 | 从大量的数据中采样 |
09 | distinct()去重 | 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中。 |
10 | coalesce()合并分区 | def coalesce(numPartitions: Int, shuffle: Boolean = false, //默认false不执行shuffle partitionCoalescer.Option[PartitionCoalescer] = Option.empty) (implicit ord: Ordering[T] = null) : RDD[T] | 缩减分区数,用于大数据集过滤后,提高小数据集的执行效率。 |
11 | repartition()重新分区(执行Shuffle) | def repartition(numPartitions: Int)(implicit ord: Ordering[T] = null): RDD[T] | 该操作内部其实执行的是coalesce操作,参数shuffle的默认值为true。无论是将分区数多的RDD转换为分区数少的RDD,还是将分区数少的RDD转换为分区数多的RDD,repartition操作都可以完成,因为无论如何都会经shuffle过程。 |
12 | sortBy()排序 | 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的分区数一致。 |
13 | pipe()调用脚本 | def pipe(command: String): RDD[String] | 管道,针对每个分区,都调用一次shell脚本,返回输出的RDD。注意:在Worker节点可以访问到的位置脚本需要放 |
双Value类型交互
序号 | 名称 | 函数签名 | 功能说明 |
---|---|---|---|
01 | intersection()交集 | def intersection(other: RDD[T]): RDD[T] | 对源RDD和参数RDD求交集后返回一个新的RDD |
02 | union()并集不去重 | def union(other: RDD[T]): RDD[T] | 对源RDD和参数RDD求并集后返回一个新的RDD |
03 | subtract()差集 | def subtract(other: RDD[T]): RDD[T] | 计算差的一种函数,去除两个RDD中相同元素,不同的RDD将保留下来 |
04 | zip()拉链 | 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类型
序号 | 名称 | 函数签名 | 功能说明 |
---|---|---|---|
01 | partitionBy()按照K重新分区 | def partitionBy(partitioner: Partitioner): RDD[(K, V)] | 将RDD[K,V]中的K按照指定Partitioner重新进行分区;如果原有的RDD和新的RDD是一致的话就不进行分区,否则会产生Shuffle过程。 |
02 | reduceByKey()按照K聚合V | def 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的分区数。 |
03 | groupByKey()按照K重新分组 | def groupByKey(): RDD[(K, Iterable[V])] | groupByKey对每个key进行操作,但只生成一个seq,并不进行聚合。该操作可以指定分区器或者分区数(默认使用HashPartitioner) |
04 | aggregateByKey()按照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处理分区内和分区间逻辑 |
05 | foldByKey()分区内和分区间相同的aggregateByKey() | def foldByKey(zeroValue: V)(func: (V, V) => V): RDD[(K, V)] 参数zeroValue:是一个初始化值,它可以是任意类型 参数func:是一个函数,两个输入参数相同 | aggregateByKey的简化操作,seqop和combop相同。即,分区内逻辑和分区间逻辑相同。 |
06 | combineByKey()转换结构后分区内和分区间操作 | 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合并成一个集合。 |
07 | sortByKey()按照K进行排序 | def sortByKey(ascending: Boolean = true, // 默认,升序 numPartitions: Int =self.partitions.length) : RDD[(K, V)] | 在一个(K,V)的RDD上调用,K必须实现Ordered接口,返回一个按照key进行排序的(K,V)的RDD |
08 | mapValues()只对V进行操作 | def mapValues[U](f: V => U): RDD[(K, U)] | 针对于(K,V)形式的类型只对V进行操作 |
09 | join()等同于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 |
10 | cogroup()类似于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行动算子
序号 | 名称 | 函数签名 | 功能说明 |
---|---|---|---|
01 | reduce()聚合 | def reduce(f: (T, T) => T): T | f函数聚集RDD中的所有元素,先聚合分区内数据,再聚合分区间数据。 |
02 | collect()以数组的形式返回数据集 | def collect(): Array[T] | 在驱动程序中,以数组Array的形式返回数据集的所有元素。 |
03 | count()返回RDD中元素个数 | def count(): Long | 返回RDD中元素的个数 |
04 | first()返回RDD中的第一个元素 | def first(): T | 返回RDD中的第一个元素 |
05 | take()返回由RDD前n个元素组成的数组 | def take(num: Int): Array[T] | 返回一个由RDD的前n个元素组成的数组 |
06 | takeOrdered()返回该RDD排序后前n个元素组成的数组 | def takeOrdered(num: Int)(implicit ord: Ordering[T]): Array[T] | 返回该RDD排序后的前n个元素组成的数组 |
07 | aggregate() | def aggregate[U: ClassTag](zeroValue: U)(seqOp: (U, T) => U, combOp: (U, U) => U): U | aggregate函数将每个分区里面的元素通过分区内逻辑和初始值进行聚合,然后用分区间逻辑和初始值(zeroValue)进行操作。 |
08 | fold() | def fold(zeroValue: T)(op: (T, T) => T): T | 折叠操作,aggregate的简化操作,即,分区内逻辑和分区间逻辑相同 |
09 | countByKey()统计每种key的个数 | def countByKey(): Map[K, Long] | 统计每种key的个数 |
10 | saveAsTextFile(path)保存成Text文件 | 将数据集的元素以textfile的形式保存到HDFS文件系统或者其他支持的文件系统,对于每个元素,Spark将会调用toString方法,将它装换为文件中的文本 | |
11 | saveAsSequenceFile(path) 保存成Sequencefile文件 | 将数据集中的元素以Hadoop Sequencefile的格式保存到指定的目录下,可以使HDFS或者其他Hadoop支持的文件系统。注意:只有kv类型RDD有该操作,单值的没有 | |
12 | saveAsObjectFile(path) 序列化成对象保存到文件 | 用于将RDD中的元素序列化成对象,存储到文件中。 | |
13 | foreach(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