Spark-core--三大数据结构之RDD-转换算子

本文详细介绍了Spark中的三种关键数据结构:弹性分布式数据集(RDD)、广播变量和累加器。RDD作为Spark的基础,具备弹性、分布式、数据集和不可变等特性,支持转换和行动算子。广播变量是只读的分布式变量,优化了任务间通信。累加器则是只写的分布式变量,用于在任务执行过程中累积计算结果。通过对这些数据结构的理解,可以更好地掌握Spark的并行计算原理。
摘要由CSDN通过智能技术生成

Spark中的三种数据结构

Spark 计算框架为了能够进行高并发和高吞吐的数据处理,封装了三大数据结构,用于
处理不同的应用场景。三大数据结构分别是:

1.RDD:弹性分布式数据集
2.广播变量:分布式只读变量
3.累加器:分布式只写变量


什么是RDD?

​ RDD是弹性分布式数据集,它是Spark中最基本的数据处理模型,代码中是一个抽象类,它代表一个弹性的不可变可分区、里面的元素可并行计算的集合。:

什么叫做弹性的分布式数据集?

首先我们先从对弹性这个方面来了解

在生活上什么可以叫做弹性呢?比如一个球,在你向下拍打的时候,他会发生形变,接下来因为球内有一定气体,会使得球会恢复成原来的样子,说明球有一定的弹性

Spark-RDD是一个有弹性的数据集又怎么理解呢?

可以从存储,容错,分片,计算机内考虑

  1. 存储的弹性:内存与磁盘的自动切换;
  2. 容错的弹性:数据丢失可以自动恢复;
  3. 计算的弹性:计算出错重试机制;
  4. 分片的弹性:可根据需要重新分片

接下来对他的其他属性进行诠释一下

分布式:数据存储在大数据集群不同节点上
数据集:RDD 封装了计算逻辑,并不保存数据
数据抽象:RDD 是一个抽象类,需要子类具体实现
不可变:RDD 封装了计算逻辑,是不可以改变的,想要改变,只能产生新的 RDD,在新的 RDD 里面封装计算逻辑

Spark-RDD - 转换算子

转换算子就是在创建了一个新的RDD的时候,你想要改变这个RDD,就会创建一个新的RDD去替换旧的RDD,在值得注意的是RDD本身是不会存储数据的,这个时候可能会有疑问?不是说RDD有容错吗?你说他不存储数据,那他发生出错的时候,怎么去撤回上一步操作呢?这个时候就会说到RDD的一个血缘关系,具体的会在后续发出来,转换算子本身是不会去加载到内存中的,只有遇到行动算子,才会加载到内存中,再去被执行

那么我们先首先去学习转换算子

转换算子总汇表

MapMapPartitionsFlatMapgroupByfilterdistinct
sortBymapPartitionsWithIndexglomsamplecoalescerepartition
intersectionunionsubtractzippartitionByreduceByKey
groupByKeyaggregateByKeyfoldByKeycombineByKeysortByKeyjoin
leftOuterJoincogroup

注意!!!!

  • 在转化算子后面加上*,是我觉得比较常用的,其实每个都很重要

1.Map*

➢函数签名
def map[U: ClassTag](f: T => U): RDD[U]
➢函数说明:

将处理的数据逐条进行映射转换,这里的转换可以是类型的转换,也可以是值的转换

注:这个算子能改变RDD的结构,是一个比较常用的算子

案列

修改rdd的数据结构为(_,1)

object Spark_RDD_Map_Operator {
  def main(args: Array[String]): Unit = {
    val conf = new SparkConf().setMaster("local[*]").setAppName("Partition")
    //setMaster("local[*]")设置Spark运行模式,当前设置的是本地模式,至于后面的[*]是指
    //多少给内核[*]是给电脑的全部内核数
    val sc = new SparkContext(conf)
    val rdd = sc.makeRDD(List("Spark","Kafka","Hbase"))
    val rdd2 = rdd.map((_,1))
    println(rdd2.collect().mkString(","))

    sc.stop()
  }
}

**2. **MapPartitions

将待处理的数据以分区为单位发送到计算节点进行处理,这里的处理是指可以进行任意的处理,哪怕是过滤数据。

➢函数签名
def mapPartitionsWithIndex[U: ClassTag](
 f: (Int, Iterator[T]) => Iterator[U],
 preservesPartitioning: Boolean = false): RDD[U]
案例

取出分区内最小的数

 val rdd = sc.makeRDD(List(1, 2, 3, 4),2)
    val rdd2 = rdd.mapPartitions(data => {
      List(data.min).iterator
    })

**3. *FlatMap

➢函数说明:

将处理的数据进行扁平化后再进行映射处理,所以算子也称之为扁平映射

➢函数签名
def flatMap[U: ClassTag](f: T => TraversableOnce[U]): RDD[U]

案例

val rdd = sc.makeRDD(List(List(1, 2),List(3,4)))
    val rdd2 = rdd.flatMap(list => list)

4.groupBy*

➢函数签名
def groupBy[K](f: T => K)(implicit kt: ClassTag[K]): RDD[(K, Iterable[T])]
➢函数说明

将数据根据指定的规则进行分组, 分区默认不变,但是数据会被打乱重新组合,我们将这样
的操作称之为 shuffle。极限情况下,数据可能被分在同一个分区中

案例:
val rdd = sc.makeRDD(List("Hadoop", "Java", "Json", "HDFS"))
val rdd2 = rdd.groupBy(_.charAt(0))

5.filter*

➢函数签名
def filter(f: T => Boolean): RDD[T]
➢函数说明

将数据根据指定的规则进行筛选过滤,符合规则的数据保留,不符合规则的数据丢弃
当数据进行筛选过滤后,分区不变,但是分区内的数据可能不均衡,生产环境下,可能会出现数据倾斜

案例
val rdd: RDD[Int] = sc.makeRDD(List(1, 2, 3, 4))
 val rdd2: RDD[Int] = rdd.filter(_ > 2)

6.distinct*

➢函数签名
def distinct()(implicit ord: Ordering[T] = null): RDD[T]
def distinct(numPartitions: Int)(implicit ord: Ordering[T] = null): RDD[T]
➢函数说明

去重

这个算子比较简单就不做过多的解释了,直接看案例就好了

案例
val dataRDD2 = sc.makeRDD(List(1, 1, 2, 4, 5, 7, 7, 8))
val rdd = dataRDD2.distinct()j

7.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 的分区数一
致。中间存在 shuffle 的过程

案例
val dataRDD = sc.makeRDD(List(("1",2),("11",4),("2",5)))
val rdd = dataRDD.sortBy(tuple => tuple._1.toInt,ascending = true)
 //设置了排序是按升序
 //排序是按tuple第一元素来排的
 //为什么要toInt:原因是因为如果是String的话默认比较第一个字母,所以会导致11在2的前面,
 "11"中的"1"的ASII码比"2"的ASII码小

8.mapPartitionsWithIndex

➢函数签名
def mapPartitionsWithIndex[U: ClassTag](
 f: (Int, Iterator[T]) => Iterator[U],
 preservesPartitioning: Boolean = false): RDD[U]
➢函数说明

将待处理的数据以分区为单位发送到计算节点进行处理,这里的处理是指可以进行任意的处理,哪怕是过滤数据,在处理时同时可以获取当前分区索引。

案例
val rdd = sc.makeRDD(List(1, 2, 3, 4), 2)
    //只获取1分区的
    val rdd2 = rdd.mapPartitionsWithIndex((index, datas) => {
     datas.map(num => (num,index))
    }
    )
    println(rdd2.collect().mkString(","))

9.glom*

➢函数签名
def glom(): RDD[Array[T]]
➢函数说明

将同一个分区的数据直接转换为相同类型的内存数组进行处理,分区不变

小功能:计算所有分区最大值求和(分区内取最大值,分区间最大值求和)

val rdd1 = sc.makeRDD(List(1, 2, 3, 4), 2)
    val rdd2: RDD[Array[Int]] = rdd1.glom()
    val MaxRdd: RDD[Int] = rdd2.map(
      array => {
        array.max
      }
    )
    println(MaxRdd.collect().sum)

10.sample

➢ 函数签名
def sample(
 withReplacement: Boolean,
 fraction: Double,
 seed: Long = Utils.random.nextLong): RDD[T]
➢ 函数说明

根据指定的规则从数据集中抽取数据

val dataRDD = sparkContext.makeRDD(List(
 1,2,3,4
),1)
// 抽取数据不放回(伯努利算法)
// 伯努利算法:又叫 0、1 分布。例如扔硬币,要么正面,要么反面。
// 具体实现:根据种子和随机算法算出一个数和第二个参数设置几率比较,小于第二个参数要,大于不// 第一个参数:抽取的数据是否放回,false:不放回
// 第二个参数:抽取的几率,范围在[0,1]之间,0:全不取;1:全取;
// 第三个参数:随机数种子
val dataRDD1 = dataRDD.sample(false, 0.5)
// 抽取数据放回(泊松算法)
// 第一个参数:抽取的数据是否放回,true:放回;false:不放回
// 第二个参数:重复数据的几率,范围大于等于 0.表示每一个元素被期望抽取到的次数
// 第三个参数:随机数种子
val dataRDD2 = dataRDD.sample(true, 2)

11.coalesce

➢ 函数签名
def coalesce(numPartitions: Int, shuffle: Boolean = false,
 partitionCoalescer: Option[PartitionCoalescer] = Option.empty)
 (implicit ord: Ordering[T] = null)

: RDD[T]

➢ 函数说明

根据数据量缩减分区,用于大数据集过滤后,提高小数据集的执行效率
当 spark 程序中,存在过多的小任务的时候,可以通过 coalesce 方法,收缩合并分区,减少
分区的个数,减小任务调度成本

val dataRDD = sparkContext.makeRDD(List(1,2,3,4,1,2),6)
val dataRDD1 = dataRDD.coalesce(2)

12.repartition

➢ 函数签名
def repartition(numPartitions: Int)(implicit ord: Ordering[T] = null): RDD[T]
➢ 函数说明

该操作内部其实执行的是 coalesce 操作,参数 shuffle 的默认值为 true。无论是将分区数多的RDD 转换为分区数少的 RDD,还是将分区数少的 RDD 转换为分区数多的 RDD,repartition操作都可以完成,因为无论如何都会经 shuffle 过程。

val dataRDD = sparkContext.makeRDD(List(
 1,2,3,4,1,2
),2)
val dataRDD1 = dataRDD.repartition(4)

13.intersection*

➢ 函数签名
def intersection(other: RDD[T]): RDD[T]
➢ 函数说明

对源 RDD 和参数 RDD 求交集后返回一个新的 RDD


14.union*

➢ 函数签名
def union(other: RDD[T]): RDD[T]
➢ 函数说明

对源 RDD 和参数 RDD 求并集后返回一个新的 RDD


15.subtract*

➢ 函数签名
def subtract(other: RDD[T]): RDD[T]
➢ 函数说明

以一个 RDD 元素为主,去除两个 RDD 中重复元素,将其他元素保留下来。求差集


16.zip*

➢ 函数签名
def zip[U: ClassTag](other: RDD[U]): RDD[(T, U)]
➢ 函数说明

将两个 RDD 中**的元素,以键值对的形式进行合并。其中,键值对中的 Key 为第 1 个 RDD
中的元素,Value 为第 2 个 RDD 中的相同位置的元素。

//intersection ----> 交集
//intersection ----> 交集
    val dataRDD1 = sc.makeRDD(List(1, 2, 3, 4))
    val dataRDD2 = sc.makeRDD(List(3, 4, 5, 6))
    val dataRDD = dataRDD1.intersection(dataRDD2)
    //3,4,
    //    dataRDD.collect().foreach(x => print(x + ","))
    println(dataRDD.collect().mkString(","))

    //union  ----> 并集
    val dataRDD3 = dataRDD1.union(dataRDD2)
    //1,2,3,4,3,4,5,6
    //    dataRDD3.collect().foreach(println)
    println(dataRDD3.collect().mkString(","))
    //subtract   ----> 差
    //1,2,
    val dataRDD4 = dataRDD1.subtract(dataRDD2)
    //    dataRDD4.collect().foreach(x => print(x + ","))
    println(dataRDD4.collect().mkString(","))

    //拉链  ---->(1,3),(2,4),(3,5),(4,6),
    val dataRDD6: RDD[(Int, Int)] = dataRDD1.zip(dataRDD2)

17.partitionBy*

➢ 函数签名
def partitionBy(partitioner: Partitioner): RDD[(K, V)]
➢ 函数说明

将数据按照指定 Partitioner 重新进行分区。Spark 默认的分区器是 HashPartitioner

val rdd: RDD[(Int, String)] = sc.makeRDD(Array((1, "aaa"), (2, "bbb"), (3, "ccc"),(4,"ddd")),2)
//val rawMod = x % mod
val rdd2: RDD[(Int, String)] = rdd.partitionBy(new HashPartitioner(2))
//当前再次的话 返回还是自己
rdd.partitionBy(new HashPartitioner(2))
 rdd2.saveAsTextFile("output")

18.reduceByKey*

➢ 函数签名
def reduceByKey(func: (V, V) => V): RDD[(K, V)]
def reduceByKey(func: (V, V) => V, numPartitions: Int): RDD[(K, V)]
➢ 函数说明

可以将数据按照相同的 Key 对 Value 进行聚合 -> 这个也是比较常用

val dataRDD = sc.makeRDD(List(("1", 2), ("1", 4), ("2", 5), ("2", 6)))
 //相同的key分组 -- 参数二可以对聚合后的结果进行分区
 val reduceRDD = dataRDD.reduceByKey((x,y) => x+y)

19.groupByKey*

➢ 函数签名
//按key来进行分组
def groupByKey(): RDD[(K, Iterable[V])]
//分组后的分区
def groupByKey(numPartitions: Int): RDD[(K, Iterable[V])]
//分组后可以指定分区的分区器
def groupByKey(partitioner: Partitioner): RDD[(K, Iterable[V])]
➢ 函数说明

将数据源的数据根据 key 对 value 进行分组

val dataRDD = sc.makeRDD(List(("1", 2), ("1", 4), ("2", 5), ("2", 6)))
    //相同的key分组 -- 参数二可以对分组后的结果进行分区
    val reduceRDD = dataRDD.groupByKey(2).map(
      iter => {
        (iter._1,iter._2.sum)
    }
    )

20.aggregateByKey*

➢ 函数签名
def aggregateByKey[U: ClassTag](zeroValue: U)(seqOp: (U, V) => U,
 combOp: (U, U) => U): RDD[(K, U)]
➢ 函数说明

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

val rdd = sc.makeRDD(List(("a", 1), ("a", 2), ("a", 3), ("a", 5)), 2)
//存在函数的柯里化
//参数说明
//1.参数1指的是初始值
//2.参数2指的是区间内的计算规则
//3.参数3指的是区间间的计算规则
val rdd3: RDD[(String, Int)] = rdd.aggregateByKey(0)(
  (x, y) => math.max(x, y),
  (x, y) => x + y
)
➢图解说明

在这里插入图片描述

21.foldByKey*

➢ 函数签名
def foldByKey(zeroValue: V)(func: (V, V) => V): RDD[(K, V)]
➢函数说明

当分区内计算规则和分区间计算规则相同时,aggregateByKey 就可以简化为 foldByKey

val dataRDD1 = sparkContext.makeRDD(List(("a",1),("b",2),("c",3)))
val dataRDD2 = dataRDD1.foldByKey(0)(_+_)

22.combineByKey*

➢ 函数签名
def combineByKey[C](
 createCombiner: V => C,
 mergeValue: (C, V) => C,
 mergeCombiners: (C, C) => C): RDD[(K, C)]
➢ 函数说明

最通用的对 key-value 型 rdd 进行聚集操作的聚集函数(aggregation function)。类似于 aggregate(),combineByKey()允许用户返回值的类型与输入不一致。

 val rdd = sc.makeRDD(List(("a", 1), ("a", 2), ("b", 3), ("b", 4), ("b", 5), ("a", 6)), 2)
    //相同key的平均值
    //combineByKey有三个参数
    //1.表示将数据进行一定格式的转换
    //2.按照前面的参数进行比较规则aggerateByKey的参数类型一样
    //数据类型因为是动态的 所以要注意类型
    val RDD: RDD[(String, (Int, Int))] = rdd.combineByKey(v=>(v,1),(t:(Int,Int), v) => {
      //当是A时 (0+1,0+1)->(1+2,1+1)
      (t._1 + v, t._2 + 1)
      //第二个参数是将分区间的数据进行相加
    }, (t1:(Int,Int), t2:(Int,Int)) => {
      (t1._1 + t2._1, t1._2 + t2._2)
    })
    //(b,4)
    //(a,3)
    RDD.mapValues(t => t._1 / t._2).collect().foreach(println)

23.sortByKey*

➢ 函数签名
def sortByKey(ascending: Boolean = true, numPartitions: Int = self.partitions.length)
 : RDD[(K, V)]
➢函数说明

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

val dataRDD1 = sparkContext.makeRDD(List(("a",1),("b",2),("c",3)))
val sortRDD1: RDD[(String, Int)] = dataRDD1.sortByKey(true)
val sortRDD1: RDD[(String, Int)] = dataRDD1.sortByKey(false)//降序

24.join*

➢ 函数签名
 def join[W](other: RDD[(K, W)]): RDD[(K, (V, W))]
➢函数说明

在类型为(K,V)和(K,W)的 RDD 上调用,返回一个相同 key 对应的所有元素连接在一起的 (K,(V,W))的 RDD

val rdd: RDD[(Int, String)] = sc.makeRDD(Array((1, "a"), (2, "b"), (3, "c")))
val rdd1: RDD[(Int, Int)] = sc.makeRDD(Array((1, 4), (2, 5), (3, 6)))
rdd.join(rdd1).collect().foreach(println)

25.leftOuterJoin

➢ 函数签名
def leftOuterJoin[W](other: RDD[(K, W)]): RDD[(K, (V, Option[W]))]
➢ 函数说明

类似于 SQL 语句的左外连接

val dataRDD = sc.makeRDD(List(("1",2),("11",4)))
val rdd2 = sc.makeRDD(List(("1",3),("11",5),("2",9)))
//表的左右连接 相同的key进行value的连接 主要是取决于谁是主表
val leftOuterJoin = dataRDD.leftOuterJoin(rdd2)
val rightOuterJoin = dataRDD.rightOuterJoin(rdd2)

26.cogroup

➢ 函数签名
def cogroup[W](other: RDD[(K, W)]): RDD[(K, (Iterable[V], Iterable[W]))]
➢ 函数说明

就是相当于connection(连接)+group(分组)

val dataRDD1 = sparkContext.makeRDD(List(("a",1),("a",2),("c",3)))
val dataRDD2 = sparkContext.makeRDD(List(("a",1),("c",2),("c",3)))
val value: RDD[(String, (Iterable[Int], Iterable[Int]))] = 
dataRDD1.cogroup(dataRDD2)

后续行动算子也做出了总结
行动算子的连接就在这里!点一下就进去了

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值