史上最详细Spark-RDD算子介绍

Spark-Core

一、RDD概述

1.RDD(Resilient Distributed Dataset)

  • 弹性分布式数据集,是Spark中的最基本数据抽象

  • 在源码中是一个抽象类,代表一个弹性的,不可变、可分区、里面元素可以并行计算的集合。

  1. 弹性
    • 存储的弹性:内存与磁盘的自动切换
    • 容错的弹性:数据丢失可以自动恢复
    • 计算的弹性:计算出错重试机制
    • 分片的弹性:可根据需要重写分片
  2. 分布式
    • 数据存储在大数据集群中的不同节点上
  3. 数据集,不存储数据
    • RDD封装了计算逻辑,并不保存数据
  4. 数据抽象
    • RDD是一个抽象类,需要子类具体实现。
  5. 不可变
    • RDD封装了计算逻辑,不可以改变,想要改变只能生成新的RDD,在新的RDD中封装计算逻辑
  6. 可分区,并行计算

2.RDD的五大特性

  1. 一组分区(Partition),即是数据集的基本组成单位,标记数据来源于哪个分区

  2. 一个计算每个分区的函数

  3. RDD之间的依赖关系

  4. 一个Partitioner,即RDD的分区器,控制分区数据的流向(KV结构)

  5. 一个列表,存储存取每个Partition的优先位置,移动数据不如移动计算,除非资源不够

二、创建RDD的方式

  • RDD创建方式分为三种:sc为SparkContext,即Spark连接

    1. 从集合中创建RDD

      • sc.parallelize(集合)

      • sc.makeRDD(集合)

        makeRDD底层调用的就是parallelize

    2. 从外部存储中创建RDD

      • 从外部存储中创建RDD包括:本地文件系统,Hadoop支持数据集,如HDFS,Hbase等
      • sc.textFile(“文件路径”)
    3. 从其他RDD中转换新的RDD

      • 调用RDD转换算子,运算后产生新的RDD

三、RDD的分区规则

1.RDD从集合中创建

  • 默认:为cpu核心数的分区数量
  • 使用parallelize或makeRDD创建RDD的时候也可以指定分区的数量

分区源码:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-E2dlULNU-1628181644666)(C:\Users\Jzs\AppData\Roaming\Typora\typora-user-images\image-20210727201546272.png)]

2.RDD从外部存储中创建

  • 默认:取值为cpu核心数和2的最小值,一般为2

四、RDD的Transformation转换算子

1)value类型算子:

  • 对一个RDD生效的算子
1.map():映射
  • 功能说明:

    • 参数为一个函数f,作用于RDD中的每一个元素。

    当一个RDD执行 map(f:T=>U) 【T为每个元素的类型,U为计算后返回值的类型】

    计算的时候,会遍历RDD中的每一个数据项,依次应用函数f,产生一个新的RDD。即返回的新RDD的每一个元素都是依次应用函数f得来的。

    • 计算后的RDD不会改变原来的RDD分区
    • 一个分区内的计算是有序的,不同分区间计算是无序的
  • 代码说明:

    //创建一个1,2,3,4且分区为2 的rdd:RDD
    val rdd: RDD[Int] = sc.makeRDD(1 to 4, 2)
    
    //将rdd经过map算子转换为每个元素*2 的新RDD
    val mapRdd: RDD[Int] = rdd.map(elem => elem *2) 
    
2.mapPartitions():以分区为单位执行Map
  • 功能说明:

    • 参数(f:Iterator[T] => Iterator[U] )

      【T为每个元素的类型,U为计算后每个元素的类型】

      函数f将每个分区的元素放入一个迭代器中进行处理,返回处理后的迭代器

    • map()算子一次处理一个数据,mapPartitions一次处理一个分区的数据(批处理)

    • 计算后不改变原来的分区数

  • 代码说明:

    //创建一个RDD,分区数为2
    val rdd: RDD[Int] = sc.makeRDD(1 to 4, 2)
    
    //将一个分区的迭代器中每个元素map转换为2倍后返回新RDD  
    val rdd1 = rdd.mapPartitions(iter=>iter.map(_*2))
    
3.map()和mapPartitions()的区别
  • map()一次处理一条数据
  • mapPartitions()一次处理一个分区的数据
  • 因为mapPartitions()一次处理一个分区的数据,因此需要将整个分区数据加载到内存中,所以需要占用更多的内存资源。
4.mapPartitionsWithIndex():带分区号
  • **参数:**函数f:(Int,Iterator[T]) => Iterptor[U] 【Int表示分区编号】

  • 功能说明:

    和mapPartitions相同,但增加了分区编号

  • 代码说明

    //创建一个RDD
    val rdd: RDD[Int] = sc.makeRDD(1 to 4, 2)
    //使每个元素和所在分区号形成一个元组,组成一个新的RDD
    val indexRdd = rdd.mapPartitionsWithIndex(
        (index,iter)=>iter.map((index,_))
    )
    
5.flatMap():扁平化
  • **参数:**函数(f:list => list),将RDD中List的多个子List扁平化为一个大List

  • 功能说明:

    将RDD中的每一个元素经过函数f将数据拆分到一个大集合中返回

  • 代码说明:

    //创建一个RDD,一个集合中包含多个子集合
    val listRDD=sc.makeRDD(List(List(1,2),List(3,4),List(5,6),List(7)), 2)
    
    //通过flatMap()算子将所有子集合中数据取出放入到一个大的集合中,并收集打印
    listRDD.flatMap(list=>list).collect.foreach(println)
    
    
  • flatMap()中所有元素必须都是集合

6.golm():分区转换数据
  • 无参数

  • 功能说明:

    将RDD中的每个分区变成一个数组,返回一个新的RDD。新RDD中元素与原来的类型一致

  • 代码说明:

    //创建一个RDD,分区数为2
    val rdd = sc.makeRDD(1 to 4, 2)
    
    //将每个分区的元素转换为数组,并通过map()求出每个风区中的最大值
    val maxRdd: RDD[Int] = rdd.glom().map(_.max)
    
    //将新RDD收集并求出所有风区最大值之和
    println(maxRdd.collect().sum)
    
7.groupBy():分组
  • 参数:函数(f:T => U ) 如:elem => elem 【通过相同的元素聚合】

  • 返回值:Tuple2(elem,CompactBuffer(elem,elem,… ))

  • 功能说明:

    按照传入函数f的返回值进行分组,将计算结果相同的元素放入一个迭代器中。

  • 代码说明:

    //创建一个RDD,分区为2
    val rdd = sc.makeRDD(1 to 4, 2)
    
    //将每个%2结果相同的元素放入一个迭代器中
    rdd.groupBy(_ % 2).collect().foreach(println)
    
    //创建一个RDD
    val rdd1: RDD[String] = sc.makeRDD(List("hello","hive","hadoop","spark","scala"))
    
    //按照首字母第一个单词相同分组
    rdd1.groupBy(str=>str.charAt(0)).collect().foreach(println)
    
    //打印结果:
    (0,CompactBuffer(2, 4))
    (1,CompactBuffer(1, 3))
    (s,CompactBuffer(spark, scala))
    (h,CompactBuffer(hello, hive, hadoop))
    
8.filter():过滤
  • **参数:**函数(f:T => Boolean) 如(_%2 ==1),即保留结果为true的元素

  • 功能说明:

    ​ 将RDD中的每一个元素经过函数f后返回一个Boolean类型的结果。结果为true的元素保留,false丢弃。

  • 代码说明:

    //创建一个RDD,分区数为2
    val rdd: RDD[Int] = sc.makeRDD(Array(1, 2, 3, 4), 2)
    
    //将每个元素%2,值等于0的,即返回值为true的加入到新的RDD,返回值false的丢弃
    val filterRdd: RDD[Int] = rdd.filter(_ % 2 == 0)
    
9.sample():采样
  • 参数:

    1. withReplacement: Boolean, 【参数1:Boolean类型。true表示取出后放回抽样,所以能被重复抽到, fase表示取出后不放回,所以最多被抽1次】

    2. fraction: Double, 【参数1为false时,取值 [ 0,1 ] 表示每个元素被取出的概率

      ​ 参数1位true时,取值 [ 1-无穷 ] 表示每个元素可能被抽取的次数的概率最高 】

    3. seed: Long = Utils.random.nextLong): RDD[T] 【Seed:随机数种子,默认和当前时间戳有关,若指定随 机数种子的值,则采样后值将固定,重复执行结果不变】

  • 功能说明:

    从数据中采样

  • 代码说明:

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

    • 默认为空参,去重后的分区数和去重前一致
    • 指定参数(numPartitions:Int),可以改变去重后的分区数量
  • 功能说明:

    对内部的元素进行去重,去重后放入新的RDD中

  • 与HashSet的去重不同点:

    • HashSet通过将元素放入一个HashSet实现自动去重,但需要将所有元素全部放入一个HashSet,需要消耗大量内存,可能出现OOM(内存溢出)

    • distinct()去重的内部源码:map(x => (x, null)).reduceByKey((x, ) => x, numPartitions).map(._1)

      ​ 通过将元素map转换,再reduceByKey聚合最后map保留元素本身的方式去重

      这种方式使用了Spark算子的多分区并行操作,解决了内存溢出的问题

  • 代码说明:

    // 创建一个RDD
     val distinctRdd: RDD[Int] = sc.makeRDD(List(1,2,1,5,2,9,6,1))
    
    // 打印去重后生成的新RDD
    distinctRdd.distinct().collect().foreach(println)
    
    // 对RDD采用多个Task去重,提高并发度
    distinctRdd.distinct(2).collect().foreach(println)
    
    
  • distinc()去重会执行shuffle过程

11.coalesce():分区合并
  • 参数

    1. numPartitioons:Int 【合并后的分区数,可以大于原来的分区数,也可以小于原来的分区数】
    2. shuffle:Boolean = false 【默认为false,不走shuffle流程,true则走shuff了流程】
  • 功能说明:

    默认值为false,不走shuffle流程,适合用于减小分区数的操作。

    如果增加分区数,需要将shuffle设置为true。不走shuffle且扩大分区没有意义,元素仍在原来的分区之中。

  • 代码说明:

    // 创建一个RDD,分区数为2
    val rdd: RDD[Int] = sc.makeRDD(Array(1, 
  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值