一、架构设计

  • Driver根据用户代码构建计算流图,拆解出分布式任务并分发到 Executors 中去;每个Executors收到任务,然后处理这个 RDD 的一个数据分片子集
  • DAGScheduler根据用户代码构建 DAG;以 Shuffle 为边界切割 Stages;基于 Stages 创建 TaskSets,并将 TaskSets 提交给 TaskScheduler 请求调度
  • TaskScheduler 在初始化的过程中,会创建任务调度队列,任务调度队列用于缓存 DAGScheduler 提交的 TaskSets。TaskScheduler 结合 SchedulerBackend 提供的 WorkerOffer,按照预先设置的调度策略依次对队列中的任务进行调度,也就是把任务分发给SchedulerBackend
  • SchedulerBackend 用一个叫做 ExecutorDataMap 的数据结构,来记录每一个计算节点中 Executors 的资源状态。会与集群内所有 Executors 中的 ExecutorBackend 保持周期性通信。SchedulerBackend收到TaskScheduler过来的任务,会把任务分发给ExecutorBackend去具体执行
  • ExecutorBackend收到任务后多线程执行(一个线程处理一个Task)。处理完毕后反馈StatusUpdate给SchedulerBackend,再返回给TaskScheduler,最终给DAGScheduler

二、常用算子

2.1、RDD概念

Spark 主要以一个 弹性分布式数据集_(RDD)的概念为中心,它是一个容错且可以执行并行操作的元素的集合。有两种方法可以创建 RDD:在你的 driver program(驱动程序)中 _parallelizing 一个已存在的集合,或者在外部存储系统中引用一个数据集,例如,一个共享文件系统,HDFS,HBase,或者提供 Hadoop InputFormat 的任何数据源。

从内存创建RDD

  1. import org.apache.spark.rdd.RDD
  2. import org.apache.spark.{SparkConf, SparkContext}
  3. // 从内存创建RDD
  4. object MakeRDDFromMemory {
  5. def main(args: Array[String]): Unit = {
  6. // 准备环境
  7. val sparkConf = new SparkConf().setMaster("local[*]").setAppName("RDD")
  8. // 并行度,如果不设置则默认当前运行环境的最大可用核数
  9. sparkConf.set("spark.default.parallelism", "2")
  10. val sc = new SparkContext(sparkConf)
  11. // 从内存中创建RDD,将内存中集合的数据作为处理的数据源
  12. val seq = Seq[Int](1, 2, 3, 4, 5, 6)
  13. val rdd: RDD[Int] = sc.makeRDD(seq)
  14. rdd.collect().foreach(println)
  15. // numSlices表示分区的数量,不传默认spark.default.parallelism
  16. val rdd2: RDD[Int] = sc.makeRDD(seq, 3)
  17. // 将处理的数据保存成分区文件
  18. rdd2.saveAsTextFile("output")
  19. sc.stop()
  20. }
  21. }

从文件中创建RDD

  1. import org.apache.spark.{SparkConf, SparkContext}
  2. // 从文件中创建RDD(本地文件、HDFS文件)
  3. object MakeRDDFromTextFile {
  4. def main(args: Array[String]): Unit = {
  5. // 准备环境
  6. val sparkConf = new SparkConf().setMaster("local[*]").setAppName("RDD")
  7. val sc = new SparkContext(sparkConf)
  8. // 从文件中创建RDD,将文件中的数据作为处理的数据源
  9. // path路径默认以当前环境的根路径为基准。可以写绝对路径,也可以写相对路径
  10. //val rdd: RDD[String] = sc.textFile("datas/1.txt")
  11. // path路径可以是文件的具体路径,也可以目录名称
  12. //val rdd = sc.textFile("datas")
  13. // path路径还可以使用通配符 *
  14. //val rdd = sc.textFile("datas/1*.txt")
  15. // path还可以是分布式存储系统路径:HDFS
  16. val rdd = sc.textFile("hdfs://localhost:8020/test.txt")
  17. rdd.collect().foreach(println)
  18. sc.stop()
  19. }
  20. }

2.2、常用算子

map算子:数据转换

  1. import org.apache.spark.rdd.RDD
  2. import org.apache.spark.{SparkConf, SparkContext}
  3. // map算子
  4. object map {
  5. def main(args: Array[String]): Unit = {
  6. val sparkConf = new SparkConf().setMaster("local[*]").setAppName("Operator")
  7. val sc = new SparkContext(sparkConf)
  8. val rdd = sc.makeRDD(List(1, 2, 3, 4))
  9. // 转换函数
  10. def mapFunction(num: Int): Int = {
  11. num * 2
  12. }
  13. // 多种方式如下
  14. // val mapRDD: RDD[Int] = rdd.map(mapFunction)
  15. // val mapRDD: RDD[Int] = rdd.map((num: Int) => {
  16. // num * 2
  17. // })
  18. // val mapRDD: RDD[Int] = rdd.map((num: Int) => num * 2)
  19. // val mapRDD: RDD[Int] = rdd.map((num) => num * 2)
  20. // val mapRDD: RDD[Int] = rdd.map(num => num * 2)
  21. val mapRDD: RDD[Int] = rdd.map(_ * 2)
  22. mapRDD.collect().foreach(println)
  23. sc.stop()
  24. }
  25. }

mapPartitions算子:数据转换(分区批处理)

  1. import org.apache.spark.rdd.RDD
  2. import org.apache.spark.{SparkConf, SparkContext}
  3. /**
  4. * mapPartitions VS map
  5. *
  6. * map 传入的是分区中的每个元素,是对每个元素就进行一次转换和改变,但不会减少或增多元素
  7. * mapPartitions 传入的参数是Iterator返回值也是Iterator,所传入的计算逻辑是对一个Iterator进行一次运算,可以增加或减少元素
  8. *
  9. *
  10. * Map 算子因为类似于串行操作,所以性能比较低,而是 mapPartitions 算子类似于批处理,所以性能较高。
  11. * 但是 mapPartitions 算子会长时间占用内存,这样会导致内存OOM。而map会在内存不够时进行GC。
  12. *
  13. * 详细参考 https://blog.csdn.net/AnameJL/article/details/121689987
  14. */
  15. object mapPartitions {
  16. def main(args: Array[String]): Unit = {
  17. val sparkConf = new SparkConf().setMaster("local[*]").setAppName("Operator")
  18. val sc = new SparkContext(sparkConf)
  19. val rdd = sc.makeRDD(List(1, 2, 3, 4), 2)
  20. // mapPartitions: 可以以分区为单位进行数据转换操作,但是会将整个分区的数据加载到内存进行引用。
  21. // 在内存较小,数据量较大的场合下,容易出现内存溢出。
  22. val mpRDD: RDD[Int] = rdd.mapPartitions(iter => {
  23. println("批处理当前分区数据")
  24. iter.map(_ * 2)
  25. })
  26. mpRDD.collect().foreach(println)
  27. sc.stop()
  28. }
  29. }

mapPartitionsWithIndex算子:分区索引 + 数据迭代器

  1. import org.apache.spark.{SparkConf, SparkContext}
  2. // 分区索引
  3. object mapPartitionsWithIndex {
  4. def main(args: Array[String]): Unit = {
  5. val sparkConf = new SparkConf().setMaster("local[*]").setAppName("Operator")
  6. val sc = new SparkContext(sparkConf)
  7. val rdd = sc.makeRDD(List(1, 2, 3, 4), 2)
  8. val mpiRDD = rdd.mapPartitionsWithIndex(
  9. //(分区索引, 数据迭代器)
  10. (index, iter) => {
  11. println("index:" + index, "iter[" + iter.mkString(",") + "]")
  12. }
  13. )
  14. mpiRDD.collect().foreach(println)
  15. sc.stop()
  16. }
  17. }

flatMap算子:数据扁平化

  1. import org.apache.spark.rdd.RDD
  2. import org.apache.spark.{SparkConf, SparkContext}
  3. // 将处理的数据进行扁平化后再进行映射处理,所以算子也称之为扁平映射
  4. object flatMap {
  5. def main(args: Array[String]): Unit = {
  6. val sparkConf = new SparkConf().setMaster("local[*]").setAppName("Operator")
  7. val sc = new SparkContext(sparkConf)
  8. val rdd: RDD[List[Int]] = sc.makeRDD(List(
  9. List(1, 2), List(3, 4)
  10. ))
  11. // 多个list合并成一个list
  12. val flatRDD: RDD[Int] = rdd.flatMap(list => list)
  13. flatRDD.collect().foreach(println)
  14. sc.stop()
  15. }
  16. }

glom算子:分区内数据合并

  1. import org.apache.spark.rdd.RDD
  2. import org.apache.spark.{SparkConf, SparkContext}
  3. // 将同一个分区的数据直接转换为相同类型的内存数组进行处理,分区不变
  4. object glom {
  5. def main(args: Array[String]): Unit = {
  6. val sparkConf = new SparkConf().setMaster("local[*]").setAppName("Operator")
  7. val sc = new SparkContext(sparkConf)
  8. val rdd: RDD[Int] = sc.makeRDD(List(1, 2, 3, 4), 2)
  9. // 把每一个分区内数据合并成Array
  10. val glomRDD: RDD[Array[Int]] = rdd.glom()
  11. glomRDD.collect().foreach(array => {
  12. println(array.mkString(","))
  13. })
  14. sc.stop()
  15. }
  16. }

groupBy算子:数据分组

  1. import org.apache.spark.rdd.RDD
  2. import org.apache.spark.{SparkConf, SparkContext}
  3. // 将数据根据指定的规则进行分组, 分区默认不变,但是数据会被打乱重新组合,我们将这样的操作称之为 shuffle。
  4. // 极限情况下,数据可能被分在同一个分区中一个组的数据在一个分区中,但是并不是说一个分区中只有一个组,分组和分区没有必然的关系
  5. object groupBy {
  6. def main(args: Array[String]): Unit = {
  7. val sparkConf = new SparkConf().setMaster("local[*]").setAppName("Operator")
  8. val sc = new SparkContext(sparkConf)
  9. val rdd: RDD[Int] = sc.makeRDD(List(1, 2, 3, 4), 2)
  10. // groupBy会将数据源中的每一个数据进行分组判断,根据返回的分组key进行分组,相同的key值的数据会放置在一个组中
  11. // val groupRDD: RDD[(Int, Iterable[Int])] = rdd.groupBy(num => num % 2)
  12. val groupRDD: RDD[(Int, Iterable[Int])] = rdd.groupBy(_ % 2)
  13. groupRDD.collect().foreach(println)
  14. sc.stop()
  15. }
  16. }

filter算子:数据过滤

  1. import org.apache.spark.rdd.RDD
  2. import org.apache.spark.{SparkConf, SparkContext}
  3. // 将数据根据指定的规则进行筛选过滤,符合规则的数据保留,不符合规则的数据丢弃。
  4. // 当数据进行筛选过滤后,分区不变,但是分区内的数据可能不均衡,生产环境下,可能会出现数据倾斜。
  5. object filter {
  6. def main(args: Array[String]): Unit = {
  7. val sparkConf = new SparkConf().setMaster("local[*]").setAppName("Operator")
  8. val sc = new SparkContext(sparkConf)
  9. val rdd = sc.makeRDD(List(1, 2, 3, 4))
  10. val filterRDD: RDD[Int] = rdd.filter(num => num % 2 != 0)
  11. filterRDD.collect().foreach(println)
  12. sc.stop()
  13. }
  14. }

sample算子:数据采样随机抽取

  1. import org.apache.spark.{SparkConf, SparkContext}
  2. // 根据指定的规则从数据集中抽取数据
  3. object sample {
  4. def main(args: Array[String]): Unit = {
  5. val sparkConf = new SparkConf().setMaster("local[*]").setAppName("Operator")
  6. val sc = new SparkContext(sparkConf)
  7. val dataRDD = sc.makeRDD(List(1, 2, 3, 4, 5, 6, 7, 8, 9, 10), 1)
  8. // 抽取数据不放回(伯努利算法)
  9. // 伯努利算法:又叫 0、 1 分布。例如扔硬币,要么正面,要么反面。
  10. // 具体实现:根据种子和随机算法算出一个数和第二个参数设置几率比较,小于第二个参数要,大于不要
  11. // 第一个参数:抽取的数据是否放回, false:不放回
  12. // 第二个参数:抽取的几率,范围只能在[0,1]之间,0:全不取; 1:全取;
  13. // 第三个参数:随机数种子
  14. val dataRDD1 = dataRDD.sample(false, 0.5)
  15. // 抽取数据放回(泊松算法)
  16. // 第一个参数:抽取的数据是否放回, true:放回; false:不放回
  17. // 第二个参数:重复数据的几率,范围大于等于0,可以大于1 表示每一个元素被期望抽取到的次数
  18. // 第三个参数:随机数种子
  19. // 例如数据集内有10个,fraction为1的话抽取10个, 0.5的话抽取5个,2的话抽取20个
  20. val dataRDD2 = dataRDD.sample(true, 2)
  21. println(dataRDD1.collect().mkString(","))
  22. println(dataRDD2.collect().mkString(","))
  23. sc.stop()
  24. }
  25. }

distinct算子:数据去重


  1. import org.apache.spark.rdd.RDD
  2. import org.apache.spark.{SparkConf, SparkContext}
  3. object distinct {
  4. def main(args: Array[String]): Unit = {
  5. val sparkConf = new SparkConf().setMaster("local[*]").setAppName("Operator")
  6. val sc = new SparkContext(sparkConf)
  7. val rdd = sc.makeRDD(List(1, 2, 3, 4, 1, 2, 3, 4))
  8. val rdd1: RDD[Int] = rdd.distinct()
  9. val rdd2: RDD[Int] = rdd.distinct(2)
  10. // 底层相当于这样写
  11. val rdd3 = rdd.map(x => (x, null)).reduceByKey((x, _) => x).map(_._1)
  12. println(rdd.collect().mkString(","))
  13. println(rdd1.collect().mkString(","))
  14. println(rdd2.collect().mkString(","))
  15. println(rdd3.collect().mkString(","))
  16. sc.stop()
  17. }
  18. }

coalesce算子:数据(shuffle可选)重新分区


  1. import org.apache.spark.rdd.RDD
  2. import org.apache.spark.{SparkConf, SparkContext}
  3. /**
  4. * 根据数据量缩减分区,用于大数据集过滤后,提高小数据集的执行效率
  5. * 当 spark 程序中,存在过多的小任务的时候,可以通过 coalesce 方法,收缩合并分区,减少分区的个数,减小任务调度成本
  6. */
  7. object coalesce {
  8. def main(args: Array[String]): Unit = {
  9. val sparkConf = new SparkConf().setMaster("local[*]").setAppName("Operator")
  10. val sc = new SparkContext(sparkConf)
  11. // 默认3个分区
  12. val rdd = sc.makeRDD(List(1, 2, 3, 4, 5, 6), 3)
  13. // coalesce方法默认情况下不会将分区的数据打乱重新组合,默认shuffer=false
  14. // 这种情况下的缩减分区可能会导致数据不均衡,出现数据倾斜,如果想要让数据均衡,可以进行shuffle处理
  15. // 缩减成2个分区并shuffer
  16. val newRDD: RDD[Int] = rdd.coalesce(2, true)
  17. newRDD.saveAsTextFile("output")
  18. sc.stop()
  19. }
  20. }

repartition算子:数据shuffle重新分区

  1. import org.apache.spark.rdd.RDD
  2. import org.apache.spark.{SparkConf, SparkContext}
  3. /**
  4. * 该操作内部其实执行的是 coalesce 操作,参数 shuffle 的默认值为 true。
  5. * 无论是将分区数多的RDD 转换为分区数少的 RDD,还是将分区数少的 RDD 转换为分区数多的 RDD,
  6. * repartition操作都可以完成,因为无论如何都会经 shuffle 过程。
  7. * 直接用repartition就行,coalesce就别用了
  8. */
  9. object repartition {
  10. def main(args: Array[String]): Unit = {
  11. val sparkConf = new SparkConf().setMaster("local[*]").setAppName("Operator")
  12. val sc = new SparkContext(sparkConf)
  13. val rdd = sc.makeRDD(List(1, 2, 3, 4, 5, 6, 7, 8, 9, 10), 3)
  14. // coalesce算子可以扩大分区的,但是如果不进行shuffle操作,是没有意义,不起作用。
  15. // 所以如果想要实现扩大分区的效果,需要使用shuffle操作
  16. /**
  17. * 底层就是coalesce
  18. * def repartition(numPartitions: Int)(implicit ord: Ordering[T] = null): RDD[T] = withScope {
  19. * coalesce(numPartitions, shuffle = true)
  20. * }
  21. */
  22. // 缩减分区
  23. val newRDD1: RDD[Int] = rdd.repartition(2)
  24. // 扩大分区
  25. val newRDD2: RDD[Int] = rdd.repartition(4)
  26. rdd.saveAsTextFile("output0")
  27. newRDD1.saveAsTextFile("output1")
  28. newRDD2.saveAsTextFile("output2")
  29. sc.stop()
  30. }
  31. }

sortBy算子:数据排序

  1. import org.apache.spark.rdd.RDD
  2. import org.apache.spark.{SparkConf, SparkContext}
  3. /**
  4. * 该操作用于排序数据。在排序之前,可以将数据通过 f 函数进行处理,之后按照 f 函数处理的结果进行排序,默认为升序排列。
  5. * 排序后新产生的 RDD 的分区数与原 RDD 的分区数一致。 中间存在shuffle的过程。
  6. */
  7. object sortBy {
  8. def main(args: Array[String]): Unit = {
  9. val sparkConf = new SparkConf().setMaster("local[*]").setAppName("Operator")
  10. val sc = new SparkContext(sparkConf)
  11. // 例子1
  12. val rdd = sc.makeRDD(List(6, 2, 4, 5, 3, 1), 2)
  13. val newRDD: RDD[Int] = rdd.sortBy(n => n)
  14. println(newRDD.collect().mkString(","))
  15. newRDD.saveAsTextFile("output")
  16. // 例子2
  17. val rdd2 = sc.makeRDD(List(("1", 1), ("3", 2), ("2", 3)), 2)
  18. // sortBy方法可以根据指定的规则对数据源中的数据进行排序,默认为升序,第二个参数可以改变排序的方式
  19. // sortBy默认情况下,不会改变分区。但是中间存在shuffle操作
  20. val newRDD1 = rdd2.sortBy(t => t._1.toInt, false) // 降序
  21. val newRDD2 = rdd2.sortBy(t => t._1.toInt, true) // 升序
  22. newRDD1.collect().foreach(println)
  23. newRDD2.collect().foreach(println)
  24. sc.stop()
  25. }
  26. }

intersection union subtract zip:两个数据源 交 并 差 拉链

  1. /**
  2. * 两个数据源 交 并 差 拉链
  3. */
  4. object intersection_union_subtract_zip {
  5. def main(args: Array[String]): Unit = {
  6. val sparkConf = new SparkConf().setMaster("local[*]").setAppName("Operator")
  7. val sc = new SparkContext(sparkConf)
  8. // 交集,并集和差集要求两个数据源数据类型保持一致
  9. val rdd1 = sc.makeRDD(List(1, 2, 3, 4))
  10. val rdd2 = sc.makeRDD(List(3, 4, 5, 6))
  11. // 交集 : 【3,4】
  12. val rdd3: RDD[Int] = rdd1.intersection(rdd2)
  13. println(rdd3.collect().mkString(","))
  14. // 并集 : 【1,2,3,4,3,4,5,6】
  15. val rdd4: RDD[Int] = rdd1.union(rdd2)
  16. println(rdd4.collect().mkString(","))
  17. // 差集 : 【1,2】
  18. val rdd5: RDD[Int] = rdd1.subtract(rdd2)
  19. println(rdd5.collect().mkString(","))
  20. // 拉链 : 【1-3,2-4,3-5,4-6】
  21. val rdd6: RDD[(Int, Int)] = rdd1.zip(rdd2)
  22. println(rdd6.collect().mkString(","))
  23. // 拉链操作两个数据源的类型可以不一致,但要求分区中数据数量保持一致
  24. val rdd7 = sc.makeRDD(List("a", "b", "c", "d"))
  25. val rdd8 = rdd1.zip(rdd7)
  26. println(rdd8.collect().mkString(","))
  27. sc.stop()
  28. }
  29. }

partitionBy算子:数据按照指定规则重新进行分区

  1. import org.apache.spark.rdd.RDD
  2. import org.apache.spark.{HashPartitioner, SparkConf, SparkContext}
  3. /**
  4. * partitionBy:数据按照指定规则重新进行分区。Spark 默认的分区器是 HashPartitioner
  5. * repartition coalesce:将分区增加或缩小,数据是无规则的
  6. */
  7. object partitionBy {
  8. def main(args: Array[String]): Unit = {
  9. val sparkConf = new SparkConf().setMaster("local[*]").setAppName("Operator")
  10. val sc = new SparkContext(sparkConf)
  11. val rdd = sc.makeRDD(List(1, 2, 3, 4), 2)
  12. // PairRDDFunctions才支持partitionBy,所以需要先转换成mapRDD
  13. val mapRDD: RDD[(Int, Int)] = rdd.map(num => (num, 1))
  14. // partitionBy根据指定的分区规则对数据进行重分区
  15. val newRDD = mapRDD.partitionBy(new HashPartitioner(2))
  16. newRDD.partitionBy(new HashPartitioner(2))
  17. newRDD.saveAsTextFile("output")
  18. sc.stop()
  19. }
  20. }

reduceByKey算子:按相同key聚合

  1. import org.apache.spark.rdd.RDD
  2. import org.apache.spark.{SparkConf, SparkContext}
  3. /**
  4. * 可以将数据按照相同的 Key 对 Value 进行聚合
  5. */
  6. object reduceByKey {
  7. def main(args: Array[String]): Unit = {
  8. val sparkConf = new SparkConf().setMaster("local[*]").setAppName("Operator")
  9. val sc = new SparkContext(sparkConf)
  10. val rdd = sc.makeRDD(List(
  11. ("a", 1), ("a", 2), ("a", 3), ("b", 4)
  12. ))
  13. // reduceByKey : 相同的key的数据进行value数据的聚合操作
  14. // scala语言中一般的聚合操作都是两两聚合,spark基于scala开发的,所以它的聚合也是两两聚合
  15. // reduceByKey中如果key的数据只有一个,是不会参与运算的。
  16. val reduceRDD: RDD[(String, Int)] = rdd.reduceByKey((x: Int, y: Int) => {
  17. println(s"x = ${x}, y = ${y}")
  18. x + y
  19. })
  20. reduceRDD.collect().foreach(println)
  21. sc.stop()
  22. }
  23. }

groupByKey算子:根据key对数据分组

  1. import org.apache.spark.rdd.RDD
  2. import org.apache.spark.{HashPartitioner, SparkConf, SparkContext}
  3. /**
  4. * 将数据源的数据根据 key 对 value 进行分组
  5. *
  6. *
  7. * reduceByKey 和 groupByKey的区别?
  8. *
  9. * 从 shuffle 的角度: reduceByKey 和 groupByKey 都存在 shuffle 的操作,但是 reduceByKey
  10. * 可以在 shuffle 前对分区内相同 key 的数据进行预聚合(combine)功能,这样会减少落盘的数据量。
  11. * 而 groupByKey 只是进行分组,不存在数据量减少的问题, reduceByKey 性能比较高。
  12. *
  13. * 从功能的角度: reduceByKey 其实包含分组和聚合的功能。 groupByKey 只能分组,不能聚合。
  14. * 所以在分组聚合的场合下,推荐使用 reduceByKey。如果仅仅是分组而不需要聚合,那么还是只能使用 groupByKey。
  15. */
  16. object groupByKey {
  17. def main(args: Array[String]): Unit = {
  18. val sparkConf = new SparkConf().setMaster("local[*]").setAppName("Operator")
  19. val sc = new SparkContext(sparkConf)
  20. val rdd = sc.makeRDD(List(
  21. ("a", 1), ("a", 2), ("a", 3), ("b", 4)
  22. ))
  23. // groupByKey : 将数据源中的数据,相同key的数据分在一个组中,形成一个对偶元组
  24. // 元组中的第一个元素就是key,
  25. // 元组中的第二个元素就是相同key的value的集合
  26. val groupRDD: RDD[(String, Iterable[Int])] = rdd.groupByKey()
  27. groupRDD.collect().foreach(println)
  28. val groupRDD2: RDD[(String, Iterable[(String, Int)])] = rdd.groupBy(_._1)
  29. groupRDD2.collect().foreach(println)
  30. val groupRDD3 = rdd.groupByKey(2)
  31. val groupRDD4 = rdd.groupByKey(new HashPartitioner(2))
  32. sc.stop()
  33. }
  34. }

aggregateByKey算子:将数据根据不同的规则进行分区内计算和分区间计算

  1. import org.apache.spark.{SparkConf, SparkContext}
  2. /**
  3. * 将数据根据不同的规则进行分区内计算和分区间计算
  4. *
  5. */
  6. object aggregateByKey {
  7. def main(args: Array[String]): Unit = {
  8. val sparkConf = new SparkConf().setMaster("local[*]").setAppName("Operator")
  9. val sc = new SparkContext(sparkConf)
  10. val rdd = sc.makeRDD(List(
  11. ("a", 1), ("a", 2), ("a", 3), ("a", 4)
  12. ), 2)
  13. // aggregateByKey存在函数柯里化,有两个参数列表
  14. // 第一个参数列表,需要传递一个参数,表示为初始值
  15. // 主要用于当碰见第一个key的时候,和value进行分区内计算
  16. // 第二个参数列表需要传递2个参数
  17. // 第一个参数表示分区内计算规则
  18. // 第二个参数表示分区间计算规则
  19. // 取出每个分区内相同key的最大值 然后分区间相加
  20. rdd.aggregateByKey(0)((x, y) => math.max(x, y), (x, y) => x + y)
  21. .collect.foreach(println)
  22. sc.stop()
  23. }
  24. }

foldByKey算子:和aggregateByKey类似

  1. import org.apache.spark.{SparkConf, SparkContext}
  2. /**
  3. * 当分区内计算规则和分区间计算规则相同时,aggregateByKey就可以简化为foldByKey
  4. */
  5. object foldByKey {
  6. def main(args: Array[String]): Unit = {
  7. val sparkConf = new SparkConf().setMaster("local[*]").setAppName("Operator")
  8. val sc = new SparkContext(sparkConf)
  9. val rdd = sc.makeRDD(List(
  10. ("a", 1), ("a", 2), ("b", 3),
  11. ("b", 4), ("b", 5), ("a", 6)
  12. ), 2)
  13. // rdd.aggregateByKey(0)(_+_, _+_).collect.foreach(println)
  14. // 如果聚合计算时,分区内和分区间计算规则相同,spark提供了简化的方法,用下面的替换上面的
  15. rdd.foldByKey(0)(_ + _).collect.foreach(println)
  16. sc.stop()
  17. }
  18. }

combineByKey算子:和aggregateByKey类似

  1. import org.apache.spark.rdd.RDD
  2. import org.apache.spark.{SparkConf, SparkContext}
  3. /**
  4. * 最通用的对 key-value 型 rdd 进行聚集操作的聚集函数(aggregation function)。
  5. * 类似于aggregate(), combineByKey()允许用户返回值的类型与输入不一致。
  6. */
  7. object combineByKey {
  8. def main(args: Array[String]): Unit = {
  9. val sparkConf = new SparkConf().setMaster("local[*]").setAppName("Operator")
  10. val sc = new SparkContext(sparkConf)
  11. val rdd = sc.makeRDD(List(
  12. ("a", 1), ("a", 2), ("b", 3),
  13. ("b", 4), ("b", 5), ("a", 6)
  14. ), 2)
  15. // combineByKey : 方法需要三个参数
  16. // 第一个参数表示:将相同key的第一个数据进行结构的转换,实现操作
  17. // 第二个参数表示:分区内的计算规则
  18. // 第三个参数表示:分区间的计算规则
  19. val newRDD: RDD[(String, (Int, Int))] = rdd.combineByKey(
  20. v => (v, 1),
  21. (t: (Int, Int), v) => {
  22. (t._1 + v, t._2 + 1)
  23. },
  24. (t1: (Int, Int), t2: (Int, Int)) => {
  25. (t1._1 + t2._1, t1._2 + t2._2)
  26. }
  27. )
  28. val resultRDD: RDD[(String, Int)] = newRDD.mapValues {
  29. case (num, cnt) => {
  30. num / cnt
  31. }
  32. }
  33. resultRDD.collect().foreach(println)
  34. sc.stop()
  35. }
  36. }

reduceByKey、 foldByKey、 aggregateByKey、 combineByKey 的区别

  1. reduceByKey: 相同 key 的第一个数据不进行任何计算,分区内和分区间计算规则相同
  2. foldByKey: 相同 key 的第一个数据和初始值进行分区内计算,分区内和分区间计算规则相同
  3. aggregateByKey:相同 key 的第一个数据和初始值进行分区内计算,分区内和分区间计算规则可以不相同
  4. combineByKey:当计算时,发现数据结构不满足要求时,可以让第一个数据转换结构,分区内和分区间计算规则不相同

join算子:相同key连接

  1. import org.apache.spark.rdd.RDD
  2. import org.apache.spark.{SparkConf, SparkContext}
  3. /**
  4. * 在类型为(K,V)和(K,W)的 RDD 上调用,返回一个相同 key 对应的所有元素连接在一起的(K,(V,W))的 RDD
  5. */
  6. object join {
  7. def main(args: Array[String]): Unit = {
  8. val sparkConf = new SparkConf().setMaster("local[*]").setAppName("Operator")
  9. val sc = new SparkContext(sparkConf)
  10. val rdd1 = sc.makeRDD(List(
  11. ("a", 1), ("a", 2), ("c", 3), ("b", 3)
  12. ))
  13. val rdd2 = sc.makeRDD(List(
  14. ("a", 5), ("c", 6), ("a", 4)
  15. ))
  16. // join : 两个不同数据源的数据,相同的key的value会连接在一起,形成元组
  17. // 如果两个数据源中key没有匹配上,那么数据不会出现在结果中
  18. // 如果两个数据源中key有多个相同的,会依次匹配,可能会出现笛卡尔乘积,数据量会几何性增长,会导致性能降低。
  19. val joinRDD: RDD[(String, (Int, Int))] = rdd1.join(rdd2)
  20. joinRDD.collect().foreach(println)
  21. sc.stop()
  22. }
  23. }

leftOuterJoin rightOuterJoin:左外连接 右外连接

  1. import org.apache.spark.{SparkConf, SparkContext}
  2. /**
  3. * 左外连接 右外连接
  4. */
  5. object leftOuterJoin_rightOuterJoin {
  6. def main(args: Array[String]): Unit = {
  7. val sparkConf = new SparkConf().setMaster("local[*]").setAppName("Operator")
  8. val sc = new SparkContext(sparkConf)
  9. val rdd1 = sc.makeRDD(List(
  10. ("a", 1), ("b", 2) //, ("c", 3)
  11. ))
  12. val rdd2 = sc.makeRDD(List(
  13. ("a", 4), ("b", 5), ("c", 6)
  14. ))
  15. val leftJoinRDD = rdd1.leftOuterJoin(rdd2)
  16. val rightJoinRDD = rdd1.rightOuterJoin(rdd2)
  17. leftJoinRDD.collect().foreach(println)
  18. rightJoinRDD.collect().foreach(println)
  19. sc.stop()
  20. }
  21. }

cogroup算子:分组 连接

  1. import org.apache.spark.rdd.RDD
  2. import org.apache.spark.{SparkConf, SparkContext}
  3. /**
  4. * 分组 连接
  5. */
  6. object cogroup {
  7. def main(args: Array[String]): Unit = {
  8. val sparkConf = new SparkConf().setMaster("local[*]").setAppName("Operator")
  9. val sc = new SparkContext(sparkConf)
  10. val rdd1 = sc.makeRDD(List(
  11. ("a", 1), ("b", 2) //, ("c", 3)
  12. ))
  13. val rdd2 = sc.makeRDD(List(
  14. ("a", 4), ("b", 5), ("c", 6), ("c", 7)
  15. ))
  16. // cogroup : connect + group (分组,连接)
  17. val cgRDD: RDD[(String, (Iterable[Int], Iterable[Int]))] = rdd1.cogroup(rdd2)
  18. cgRDD.collect().foreach(println)
  19. sc.stop()
  20. }
  21. }

参考资料:Spark中文文档尚硅谷Spark教程

调度系统:如何把握分布式计算的精髓

我的Spark学习笔记的更多相关文章

  1. Spark学习笔记之SparkRDD

    Spark学习笔记之SparkRDD 一.   基本概念 RDD(resilient distributed datasets)弹性分布式数据集. 来自于两方面 ①   内存集合和外部存储系统 ②   ...

  2. spark学习笔记总结-spark入门资料精化

    Spark学习笔记 Spark简介 spark 可以很容易和yarn结合,直接调用HDFS.Hbase上面的数据,和hadoop结合.配置很容易. spark发展迅猛,框架比hadoop更加灵活实用. ...

  3. Spark学习笔记2(spark所需环境配置

    Spark学习笔记2 配置spark所需环境 1.首先先把本地的maven的压缩包解压到本地文件夹中,安装好本地的maven客户端程序,版本没有什么要求 不需要最新版的maven客户端. 解压完成之后 ...

  4. Spark学习笔记3(IDEA编写scala代码并打包上传集群运行)

    Spark学习笔记3 IDEA编写scala代码并打包上传集群运行 我们在IDEA上的maven项目已经搭建完成了,现在可以写一个简单的spark代码并且打成jar包 上传至集群,来检验一下我们的sp ...

  5. Spark学习笔记-GraphX-1

    Spark学习笔记-GraphX-1 标签: SparkGraphGraphX图计算 2014-09-29 13:04 2339人阅读 评论(0) 收藏 举报  分类: Spark(8)  版权声明: ...

  6. Spark学习笔记3——RDD(下)

    目录 Spark学习笔记3--RDD(下) 向Spark传递函数 通过匿名内部类 通过具名类传递 通过带参数的 Java 函数类传递 通过 lambda 表达式传递(仅限于 Java 8 及以上) 常 ...

  7. Spark学习笔记0——简单了解和技术架构

    目录 Spark学习笔记0--简单了解和技术架构 什么是Spark 技术架构和软件栈 Spark Core Spark SQL Spark Streaming MLlib GraphX 集群管理器 受 ...

  8. Spark学习笔记2——RDD(上)

    目录 Spark学习笔记2--RDD(上) RDD是什么? 例子 创建 RDD 并行化方式 读取外部数据集方式 RDD 操作 转化操作 行动操作 惰性求值 Spark学习笔记2--RDD(上) 笔记摘 ...

  9. Spark学习笔记1——第一个Spark程序:单词数统计

    Spark学习笔记1--第一个Spark程序:单词数统计 笔记摘抄自 [美] Holden Karau 等著的<Spark快速大数据分析> 添加依赖 通过 Maven 添加 Spark-c ...

  10. Spark学习笔记——读写Hbase

    1.首先在Hbase中建立一张表,名字为student 参考 Hbase学习笔记——基本CRUD操作 一个cell的值,取决于Row,Column family,Column Qualifier和Ti ...

随机推荐

  1. CF1511G Chips on a Board (倍增)

    题面 原题题面 转化方便版题意: 有 n n n 堆石子,第 i i i 堆有 c i ∈ [ 1 , m ] c_i\in [1,m] ci​∈[1,m] 个石子,有 q q q 次询问,每次询问给 ...

  2. 2020/12/9 酒etf

    2020/12/9 2.315建仓酒etf,之后陆续加仓,拿到年底看看 2020/12/12 2.36卖出部分,目前成本2.106,盈利百分之9.449,白酒应该是没问题,但感觉年前应该有波调整. 2 ...

  3. 第七十三篇:解决Vue组件中的样式冲突

    好家伙, 1.组件之间的样式冲突 默认情况下,写在.vue组件中的样式会全局生效,因此很容易造成多个组件之间的样式冲突问题. 举个例子: 我们在Left.vue的组件中添加样式 <templat ...

  4. java 类名后加变量名是什么意思?

    回答这个问题我们需要先了解两个事情: A是一个类,我们如果对他进行实例化,需要这样写: A a = new A(); 详细解释一下这个语句,首先等号左边做的事情:在JVM栈内存(stack)中定义了一 ...

  5. [Python]-string-字符串

    字符串是Python中很常用的数据类型,此处记录一些典型用法并随时更新. split()方法 通过指定分隔符对字符串进行切片,如果参数 num 有指定值,则分隔 num+1 个子字符串. 两个参数st ...

  6. MySQL DDL执行方式-Online DDL介绍

    1 引言 大家好,今天与大家一起分享一下 mysql DDL执行方式. 一般来说MySQL分为DDL(定义)和DML(操作). DDL:Data Definition Language,即数据定义语言 ...

  7. ProxySQL(5):线程、线程池、连接池

    文章转载自:https://www.cnblogs.com/f-ck-need-u/p/9281909.html ProxySQL的线程 ProxySQL由多个模块组成,是一个多线程的daemon类程 ...

  8. 通过堡垒机上传文件报错ssh:没有权限的问题

    背景描述 一台有公网IP的主机安装的有jumpserver,假设为A主机,另外几台没有公网ip的主机,假设其中一个为B主机. 操作 1.通过主机A的公网IP和端口等登录到jumpserver的管理员用 ...

  9. 不停机为虚拟机添加主机磁盘(以VMware Workstation为例)

    VMware Workstation软件上安装的centos7系统,新增磁盘后使用fdisk -l命令查看不到新增的磁盘,有没有办法在不重启的情况下添加上新磁盘? 有办法 具体如下: # 查看主机总线 ...

  10. k8s上安装安装 Ingress Controller &卸载

    在 master 节点上执行 nginx-ingress.yaml文件内容 # 如果打算用于生产环境,请参考 https://github.com/nginxinc/kubernetes-ingres ...