Spark-Core核心算子_spark k-core 算法

		* [1.3 mapPartitionsWithIndex(不常用)](#13_mapPartitionsWithIndex_109)
		* [1.4 filterMap()\_扁平化(合并流)](#14_filterMap__138)
		* [1.5 groupBy()\_分组](#15_groupBy__164)
		* [1.6 filter()\_过滤](#16__filter__215)
		* [1.7 distinct()\_去重](#17_distinct__235)
		* [1.8 coalesce()\_合并分区](#18__coalesce__261)
		* [1.9 repartition()\_重新分区](#19_repartition__285)
		* [1.10 sortBy()\_排序](#110__sortBy__306)
		* [1.11 map和mapPartitions区别](#111_mapmapPartitions_343)
		* [1.12 coalesce和repartition区别](#112_coalescerepartition_366)
	- [2、双-Value类型](#2Value_381)
	- * [2.1 intersection()\_交集](#21_intersection__383)
		* [2.2 union()\_并集不去重](#22_union__405)
		* [2.3 subtract()\_差集](#23_subtract__426)
		* [2.4 zip()\_拉链](#24_zip__453)
	- [3、Key—Value类型](#3KeyValue_492)
	- * [3.1 partitionBy()\_按照K重新分区](#31_partitionBy_K_493)
		* [3.2 groupByKey()\_按照K重新分组](#32_groupByKey_K_522)
		* [3.3 reduceByKey()\_按照K聚合V](#33_reduceByKey_KV_544)
		* [3.4 aggregateByKey()\_不同逻辑的归约](#34_aggregateByKey__564)
		* [3.5 sortByKey()\_按照K进行排序](#35_sortByKey_K_589)
		* [3.6 mapValues()\_只对V进行操作](#36_mapValues_V_612)
		* [3.7 join()\_等同于sql内连接](#37_join_sql_631)
		* [3.8 cogroup()\_类似于sql全连接](#38_cogroup_sql_653)
		* [3.9 自定义分区器](#39__691)
		* [3.10 reduceByKey和groupByKey区别](#310_reduceByKeygroupByKey_722)
+ [三、行动算子(Action)](#Action_728)
+ - [1、collect()\_以数组的形式返回数据集](#1collect__732)
	- [2、count()\_返回RDD中元素个数](#2count_RDD_742)
	- [3、first()\_返回RDD中的第一个元素](#3first_RDD_750)
	- [4、take()\_返回由RDD前n个元素组成的数组](#4take_RDDn_758)
	- [5、takeOrdered()\_返回排序后前n个元素](#5takeOrdered_n_768)
	- [6、countByKey()\_统计每种key的个数](#6countByKey_key_784)
	- [7、saveAsTextFile(path)\_保存成Text文件](#7saveAsTextFilepath_Text_798)
	- [8、saveAsSequenceFile(path)\_保存成Sequencefile文件](#8saveAsSequenceFilepath_Sequencefile_810)
	- [9、saveAsObjectFile(path)\_序列化成对象保存到文件](#9saveAsObjectFilepath__821)
	- [10、foreach()\_遍历RDD中每一个元素](#10foreach_RDD_832)

一、数据源获取

1、从集合中获取
sc.parallelize(list)
sc.makeRDD(list)
sc.makeRDD(list, 2)

val list: List[Int] = List(1, 2, 3, 4, 5)
// 从List中创建RDD
val rdd01: RDD[Int] = sc.parallelize(list)
// 底层调用parallelize。从结合list中获取数据
val rdd02: RDD[Int] = sc.makeRDD(list)
// 2:分区数量为2
val rdd03: RDD[Int] = sc.makeRDD(list, 2)

2、从外部存储系统创建
// 从文件中获取
sc.textFile("input/1.txt")

// 无论文件中存储的是什么数据,读取过来都当字符串进行处理
val rdd04: RDD[String] = sc.textFile("input/1.txt")

3、从其它RDD中创建

在其它执行步骤完成后,生成新的RDD对象

val rdd05: RDD[String] = rdd04.map(_ \* 2)

4、分区规则—load数据时

从集合中创建

从文件中创建

二、转换算子(Transformation)

// 1、创建SparkConf并设置App名称
val conf = new SparkConf().setAppName("SparkCore").setMaster("local[\*]")
// 2、创建SparkContext,该对象时提交Spark APP 的入口
val sc = new SparkContext(conf)
// 3、创建RDD
val rdd: RDD[Int] = sc.makeRDD(List(1, 2, 3, 4))
// 4、具体执行步骤
val rdd01: RDD[Int] = rdd.map(x => x \* 20)
// 5、打印结果
println(rdd01.collect().toList)
// 6、关闭连接
sc.stop()

1、Value类型
1.1 map()_

在这里插入图片描述

// 4、具体执行步骤
val rdd01: RDD[Int] = rdd.map(x => x \* 20)
// 4、具体执行步骤
val rdd02: RDD[Int] = rdd01.map(_ \* 20)

1.2 mapPartitions()

以分区为单位执行的map()

在这里插入图片描述

1.3 mapPartitionsWithIndex(不常用)
  • 里面的函数针对每个分区操作,分区有多少个,函数就执行多少次。
  • 函数的第一个参数代表分区号。
  • 函数的第二个参数代表分区数据迭代器。
  /\*\*
 \*
 \* @param f 分区编号
 \* @param preservesPartitioning 分区数据迭代器
 \*/
def mapPartitionsWithIndex[U: ClassTag](
    f: (Int, Iterator[T]) => Iterator[U],
    preservesPartitioning: Boolean = false): RDD[U] = withScope {

}

rdd03.mapPartitionsWithIndex((index, items) => {
  items.map((index, _))
})
// 指定迭代器规则,并使用分区数据迭代器
rdd03.mapPartitionsWithIndex((index, items) => {
  items.map((index, _))
}, preservesPartitioning = true)

1.4 filterMap()_扁平化(合并流)

扁平化(合并流)

功能说明

  • 与map操作类似,将RDD中的每一个元素通过应用f函数依次转换为新的元素,并封装到RDD中。
  • 区别:在flatMap操作中,f函数的返回值是一个集合,并且会将每一个该集合中的元素拆分出来放到新的RDD中。

在这里插入图片描述

def flatMap[U: ClassTag](f: T => TraversableOnce[U]): RDD[U] = withScope {

}

val rdd08: RDD[List[Int]] = sc.makeRDD(List(List(1, 2), List(3, 4), List(5, 6)), 2)
val rdd09: RDD[Int] = rdd08.flatMap(list => list)
// List(1, 2, 3, 4, 5, 6)
println(rdd09.collect().toList)

1.5 groupBy()_分组

分组

按照传入函数的返回值进行分组。将相同的key对应的值放入一个迭代器。

在这里插入图片描述

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

在这里插入图片描述

def groupBy[K](f: T => K)(implicit kt: ClassTag[K]): RDD[(K, Iterable[T])] = withScope {
  groupBy[K](f, defaultPartitioner(this))
}

案例

// 3.2 将每个分区的数据放到一个数组并收集到Driver端打印
rdd.groupBy((x)=>{x%2})
// 简化
rdd.groupBy(_%2)

val rdd10: RDD[Int] = sc.makeRDD(List(1, 2, 3, 4, 5, 6, 7, 8, 9), 3)
// (0,CompactBuffer(2, 4, 6, 8))
// (1,CompactBuffer(1, 3, 5, 7, 9))
rdd10.groupBy(_ % 2).collect().foreach(println)

val rdd11: RDD[Int] = sc.makeRDD(List(1, 2, 3, 4, 5), 3)
// 按照数字相同进行分区
// (3,CompactBuffer(3))
// (4,CompactBuffer(4))
// (1,CompactBuffer(1))
// (5,CompactBuffer(5))
// (2,CompactBuffer(2))
rdd11.groupBy(a => a).collect().foreach(println)

1.6 filter()_过滤

过滤

接收一个返回值为布尔类型的函数作为参数。当某个RDD调用filter方法时,会对该RDD中每一个元素应用f函数,如果返回值类型为true,则该元素会被添加到新的RDD中。

在这里插入图片描述

rdd11.filter(a => a % 2 == 0)
rdd11.filter(_% 2 == 0)

val rdd11: RDD[Int] = sc.makeRDD(List(1, 2, 3, 4, 5, 6, 7, 8, 9), 3)
val rdd110: RDD[Int] = rdd11.filter(a => a % 2 == 0)
// List(2, 4, 6, 8)
println(rdd110.collect().toList)

1.7 distinct()_去重

去重

  • 对内部的元素去重,并将去重后的元素放到新的RDD中。
  • 默认情况下,distinct会生成与原RDD分区个数一致的分区数。
  • 用分布式的方式去重比HashSet集合方式不容易OOM。

在这里插入图片描述

// 去重
rdd.distinct()
// 去重(2并发度)
rdd.distinct(2)

val rdd12: RDD[Int] = sc.makeRDD(List(1, 2, 3, 4, 2, 3), 3)
// List(3, 4, 1, 2)
println(rdd12.distinct().collect().toList)
// List(4, 2, 1, 3)(采用多个Task提高并发读)
println(rdd12.distinct(2).collect().toList)

1.8 coalesce()_合并分区

合并分区

  • Coalesce算子包括:配置执行Shuffle和配置不执行Shuffle两种方式。
  • 缩减分区数,用于大数据集过滤后,提高小数据集的执行效率。

在这里插入图片描述

rdd13.coalesce(2)
rdd14.coalesce(2, shuffle = true)

缩减分区并执行Shuffer

val rdd14: RDD[Int] = sc.makeRDD(List(1, 2, 3, 4, 5, 6, 7, 8, 9), 3)
// 缩减分区为2个
val rdd131: RDD[Int] = rdd13.coalesce(2)
// 缩减分区为2个,并执行Shuffer
val rdd141: RDD[Int] = rdd14.coalesce(2, shuffle = true)

1.9 repartition()_重新分区

重新分区

  • 执行Shuffle。
  • 该操作内部其实执行的是coalesce操作,参数shuffle的默认值为true。
  • 无论是将分区数多的RDD转换为分区数少的RDD,还是将分区数少的RDD转换为分区数多的RDD,repartition操作都可以完成,因为无论如何都会经shuffle过程。
  • 分区规则不是hash,因为平时使用的分区都是按照hash来实现的,repartition一般是对hash的结果不满意,想要打散重新分区。

在这里插入图片描述

rdd.repartition(2)

val rdd15: RDD[Int] = sc.makeRDD(List(1, 2, 3, 4, 5, 6, 7, 8, 9), 3)
// 重新分区
val rdd151: RDD[Int] = rdd15.repartition(2)

1.10 sortBy()_排序

排序

  • 该操作用于排序数据。
  • 在排序之前,可以将数据通过f函数进行处理,之后按照f函数处理的结果进行排序,默认为正序排列。
  • 排序后新产生的RDD的分区数与原RDD的分区数一致。
  • 实现正序和倒序排序。

在这里插入图片描述

// 正序
rdd.sortBy(num => num)
// 倒叙
rdd.sortBy(num => num, ascending = false)

案例:

val rdd16: RDD[Int] = sc.makeRDD(List(1, 2, 3, 4, 5, 6, 7, 8, 9), 3)
// 重新排序,默认升序
val rdd161: RDD[Int] = rdd16.sortBy(num => num)
// 重新排序,配置降序
val rdd162: RDD[Int] = rdd16.sortBy(num => num, ascending = false)

val rdd17: RDD[(Int, Int)] = sc.makeRDD(List((1, 2), (3, 4), (5, 6)))
// 先按照第1个值升序,在按第2个值排序
val rdd171: RDD[(Int, Int)] = rdd17.sortBy(num => num)

1.11 map和mapPartitions区别

在这里插入图片描述

map与mapPartitions的区别

  • 函数针对的对象不一样
    • map的函数是针对每个元素操作
    • mapPartitions的函数是针对每个分区操作
  • 函数的返回值不一样
    • map的函数是针对每个元素操作,要求返回一个新的元素,map生成的新RDD元素个数 = 原RDD元素个数
    • mapPartitions的函数是针对分区操作,要求返回新分区的迭代器,mapPartitions生成新RDD元素个数不一定=原RDD元素个数
  • 元素内存回收的时机不一样
    • map对元素操作完成之后就可以垃圾回收了
    • mapPartitions必须要等到分区数据迭代器里面数据全部处理完成之后才会统一垃圾回收,如果分区数据比较大可能出现内存溢出,此时可以用map代替。
val rdd02: RDD[Int] = rdd01.mapPartitions(a => a.map(b => b \* 2))
val rdd03: RDD[Int] = rdd02.mapPartitions(a => a.map(_ \* 2))

1.12 coalesce和repartition区别
  • coalesce重新分区,可以选择是否进行shuffle过程。由参数shuffle: Boolean = false/true决定。
  • repartition实际上是调用的coalesce,进行shuffle。源码如下:
def repartition(numPartitions: Int)(implicit ord: Ordering[T] = null): RDD[T] = withScope {
    coalesce(numPartitions, shuffle = true)
}

  • coalesce一般为缩减分区,如果扩大分区,不使用shuffle是没有意义的,repartition扩大分区执行shuffle。
2、双-Value类型
2.1 intersection()_交集

并集不去重

  • 对源RDD和参数RDD求交集后返回一个新的RDD。
  • 利用shuffle的原理进行求交集 ,需要将所有的数据落盘shuffle 效率很低
  • 不推荐使用

在这里插入图片描述

println(rdd01.intersection(rdd02)

val rdd01: RDD[Int] = sc.makeRDD(1 to 4)
val rdd02: RDD[Int] = sc.makeRDD(4 to 8)
// 取交集
// 利用shuffle的原理进行求交集 需要将所有的数据落盘shuffle 效率很低 不推荐使用
println(rdd01.intersection(rdd02).collect().toList)

2.2 union()_并集不去重

并集不去重

  • 对源RDD和参数RDD求并集后返回一个新的RDD
  • 由于不走shuffle ,效率高 。
  • 所有会使用到

在这里插入图片描述

rdd1.union(rdd2)

val rdd01: RDD[Int] = sc.makeRDD(1 to 4)
val rdd02: RDD[Int] = sc.makeRDD(4 to 8)
// 由于不走shuffle 效率高 所有会使用到
rdd1.union(rdd2).collect().foreach(println)

2.3 subtract()_差集

差集

  • 计算差的一种函数,去除两个RDD中相同元素,不同的RDD将保留下来。
  • 同样使用shuffle的原理,将两个RDD的数据写入到相同的位置,进行求差集
  • 需要走shuffle 效率低,不推荐使用
  • 在rdd01的数据中,与rdd02相差的数据(1,2,3)

在这里插入图片描述

// 计算第一个RDD与第二个RDD的差集并打印
rdd01.subtract(rdd02)

val rdd01: RDD[Int] = sc.makeRDD(1 to 4)
val rdd02: RDD[Int] = sc.makeRDD(4 to 8)
// 同样使用shuffle的原理 将两个RDD的数据写入到相同的位置 进行求差集
// 需要走shuffle 效率低 不推荐使用
// 在rdd01的数据中,与rdd02相差的数据(1,2,3)
rdd01.subtract(rdd02).collect().foreach(println)

2.4 zip()_拉链

拉链

  • 该操作可以将两个RDD中的元素,以键值对的形式进行合并。
  • 其中,键值对中的Key为第1个RDD中的元素,Value为第2个RDD中的元素。
  • 将两个RDD组合成Key/Value形式的RDD,这里默认两个RDD的partition数量以及元素数量都相同,否则会抛出异常。

在这里插入图片描述

val rdd01: RDD[Int] = sc.makeRDD(Array(1, 2, 3), 3)
val rdd02: RDD[String] = sc.makeRDD(Array("a", "b", "c"), 3)
// List((1,a), (2,b), (3,c))
println(rdd01.zip(rdd02).collect().toList)
// List((a,1), (b,2), (c,3))
println(rdd02.zip(rdd01).collect().toList)

反例:

val rdd02: RDD[String] = sc.makeRDD(Array("a", "b", "c"), 3)

val rdd03: RDD[String] = sc.makeRDD(Array("a", "b"), 3)
// 元素个数不同,不能拉链
// SparkException: Can only zip RDDs with same number of elements in each partition
println(rdd03.zip(rdd02).collect().toList)
val rdd04: RDD[String] = sc.makeRDD(Array("a", "b", "c"), 2)
// 分区数不同,不能拉链
// java.lang.IllegalArgumentException: Can't zip RDDs with unequal numbers of partitions: List(2, 3)
println(rdd04.zip(rdd02).collect().toList)

3、Key—Value类型
3.1 partitionBy()_按照K重新分区

按照K重新分区

  • 将RDD[K,V]中的K按照指定Partitioner重新进行分区;
  • 如果原有的RDD和新的RDD是一致的话就不进行分区,否则会产生Shuffle过程。
  • 分区数量会改变。

在这里插入图片描述

// 使用hash计算方式重分区,并重分区后分区数量 = 2
rdd01.partitionBy(new HashPartitioner(2))

val rdd01: RDD[(Int, String)] = sc.makeRDD(Array((111, "aaa"), (222, "bbbb"), (333, "ccccc")), 3)
val rdd02: RDD[(Int, String)] = rdd01.partitionBy(new HashPartitioner(2))

// 打印重分区后的分区数量
// (0,(2,bbbb))
// (1,(1,aaa))
// (1,(3,ccccc))
val rdd03: RDD[(Int, (Int, String))] = rdd02.mapPartitionsWithIndex((index, datas) => {
  datas.map((index, _))
})
rdd03.collect().foreach(println)

3.2 groupByKey()_按照K重新分组

按照K重新分组

  • groupByKey对每个key进行操作,但只生成一个seq,并不进行聚合。
  • 该操作可以指定分区器或者分区数(默认使用HashPartitioner)。
  • 分区数量不会改变。

在这里插入图片描述

rdd001.groupByKey()

val rdd001: RDD[(String, Int)] = sc.makeRDD(List(("a", 1), ("b", 5), ("a", 5), ("b", 2)
val rdd002: RDD[(String, Iterable[Int])] = rdd001.groupByKey()
// (a,CompactBuffer(1, 5))
// (b,CompactBuffer(5, 2))
rdd002.collect().foreach(println)

3.3 reduceByKey()_按照K聚合V

按照K聚合V

  • 该操作可以将RDD[K,V]中的元素按照相同的K对V进行聚合。
  • 其存在多种重载形式,还可以设置新RDD的分区数。

在这里插入图片描述

rdd01.reduceByKey((v1, v2) => (v1 + v2))

val rdd01: RDD[(String, Int)] = sc.makeRDD(List(("a", 1), ("b", 5), ("a", 5), ("b", 2)))
val rdd02: RDD[(String, Int)] = rdd01.reduceByKey((v1, v2) => (v1 + v2))
// List((a,6), (b,7))
println(rdd02.collect().toList)

3.4 aggregateByKey()_不同逻辑的归约

分区内和分区间逻辑不同的归约

在这里插入图片描述

// zeroValue(初始值):给每一个分区中的每一种key一个初始值;
// seqOp(分区内):函数用于在每一个分区中用初始值逐步迭代value;
// combOp(分区间):函数用于合并每个分区中的结果。
  def aggregateByKey[U: ClassTag](zeroValue: U, partitioner: Partitioner)(seqOp: (U, V) => U,
      combOp: (U, U) => U): RDD[(K, U)] = self.withScope {
  }
// 分区初始值=0,分区内取最大值,分区间求和
rdd01.aggregateByKey(0)(math.max(_, _), _ + _)

val rdd01: RDD[(String, Int)] = sc.makeRDD(List(("a", 1), ("b", 5), ("a", 5), ("b", 2)))
// 取出每个分区相同key对应值的最大值,然后相加
val rdd02: RDD[(String, Int)] = rdd01.aggregateByKey(0)(math.max(_, _), _ + _)
// List((a,6), (b,7))
println(rdd02.collect().toList)

3.5 sortByKey()_按照K进行排序

按照K进行排序

  • 在一个(K,V)的RDD上调用,K必须实现Ordered接口,返回一个按照key进行排序的(K,V)的RDD。

在这里插入图片描述

// 按照key的正序(默认正序)
rdd01.sortByKey(ascending = true)
// 按照key的倒序排列
rdd01.sortByKey(ascending = false)

val rdd01: RDD[(Int, String)] = sc.makeRDD(Array((3, "aa"), (6, "cc"), (2, "bb"), 1, "dd"))
// 按照key的正序(默认正序)
println(rdd01.sortByKey(ascending = true).collect().toList)
// 按照key的倒序排列
println(rdd01.sortByKey(ascending = false).collect().toList)

3.6 mapValues()_只对V进行操作

只对V进行操作

  • 针对于(K,V)形式的类型只对V进行操作

在这里插入图片描述

rdd01.mapValues(_ + "|||")

val rdd01: RDD[(Int, String)] = sc.makeRDD(Array((1, "a"), (1, "d"), (2, "b"), (3, "c")))
// 对Value值添加字符串|||
// List((1,a|||), (1,d|||), (2,b|||), (3,c|||))
println(rdd01.mapValues(_ + "|||").collect().toList)

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数大数据工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年大数据全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。
img
img
img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上大数据开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新

如果你觉得这些内容对你有帮助,可以添加VX:vip204888 (备注大数据获取)
img

1, “d”), (2, “b”), (3, “c”)))
// 对Value值添加字符串|||
// List((1,a|||), (1,d|||), (2,b|||), (3,c|||))
println(rdd01.mapValues(_ + “|||”).collect().toList)




**自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。**

**深知大多数大数据工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!**

**因此收集整理了一份《2024年大数据全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。**
[外链图片转存中...(img-x8EJWDxJ-1712861033503)]
[外链图片转存中...(img-4XxVPRJq-1712861033504)]
[外链图片转存中...(img-bjNVC8xy-1712861033504)]
[外链图片转存中...(img-1ULQ3T7t-1712861033504)]
[外链图片转存中...(img-dv5NuRdi-1712861033505)]

**既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上大数据开发知识点,真正体系化!**

**由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新**

**如果你觉得这些内容对你有帮助,可以添加VX:vip204888 (备注大数据获取)**
[外链图片转存中...(img-eaXYdCT9-1712861033505)]

  • 8
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值