【Spark】Spark核心编程

本编主要基于B站尚硅谷的视频及文档做出的一些改写和添加自己的理解而编写
B站尚硅谷Spark视频讲解链接: 点击跳转.

概述

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

    2️⃣累加器:分布式共享只写变量

    3️⃣广播变量:分布式共享只读变量


RDD原理解释

什么叫做RDD?

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


特性如下:

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

RDD是最小的计算单元,RDD将程序分成一个个的Task,并将Task发送到Executor来进行计算
在这里插入图片描述


RDD数据处理的流程
在这里插入图片描述
1️⃣:RDD的数据处理方式类似于IO流,也有装饰者设计模式
2️⃣:RDD的数据只有在调用collect方法时,才会真正的执行业务逻辑操作,之前的封装全是功能的扩展
3️⃣:RDD它是不保存数据的
跳转顶部


RDD核心属性

在这里插入图片描述


➢ 分区列表
RDD 数据结构中存在分区列表,用于执行任务时并行计算,是实现分布式计算的重要属性。

在这里插入图片描述


➢ RDD 之间的依赖关系
RDD 是计算模型的封装,当需求中需要将多个计算模型进行组合时,就需要将多个 RDD 建立依赖关系

在这里插入图片描述


➢ 分区器(可选)
当数据为 KV 类型数据时,可以通过设定分区器自定义数据的分区

在这里插入图片描述


➢ 首选位置(可选)
计算数据时,可以根据计算节点的状态选择不同的节点位置进行计算
在这里插入图片描述
跳转顶部


执行原理

1️⃣:从计算的角度来讲,数据处理过程中需要计算资源(内存 & CPU)和计算模型(逻辑)。执行时,需要将计算资源和计算模型进行协调和整合。

2️⃣:Spark 框架在执行时,先申请资源,然后将应用程序的数据处理逻辑分解成一个一个的计算任务。然后将任务发到已经分配资源的计算节点上, 按照指定的计算模型进行数据计算。最后得到计算结果。

3️⃣:RDD 是 Spark 框架中用于数据处理的核心模型,接下来我们看看,在 Yarn 环境中,RDD的工作原理:


➢启动 Yarn 集群环境
在这里插入图片描述


➢Spark 通过申请资源创建调度节点和计算节点
在这里插入图片描述


➢Spark 框架根据需求将计算逻辑根据分区划分成不同的任务
在这里插入图片描述


➢调度节点将任务根据计算节点状态发送到对应的计算节点进行计算
在这里插入图片描述


从以上流程可以看出 RDD 在整个流程中主要用于将逻辑进行封装,并生成 Task 发送给Executor 节点执行计算,接下来我们就一起看看 Spark 框架中 RDD 是具体是如何进行数据处理的。

跳转顶部


创建RDD的几种方式

从集合中创建RDD

  @Test
  //集合创建
  def rddCreationLocal(): Unit = {
    //创建SparkConf
    val conf = new SparkConf().setMaster("local[6]").setAppName("rddCreationLocal")
    //创建SparkContext
    val sc = new SparkContext(conf)
    val seq1 = Seq("hello", "world", "HI")
    val seq2 = Seq(1, 2, 3)
    /**
     * parallelize:并行
     */
    //可以不指定分区数
    val rdd1: RDD[String] = sc.parallelize(seq1)
    //要指定分区数
    val rdd2: RDD[Int] = sc.makeRDD(seq2, 2)
    //输出
    rdd2.collect().foreach(println(_))
  }

问:既然使用parallelizemakeRDD都可以,那么这俩者是否有什么区别?

  • 答:其实makeRDD方法在底层实现时就是调用了rdd对象的parallelize方法
    parallelize原码展示
    在这里插入图片描述
    nakrRDD源码展示
    在这里插入图片描述
    跳转顶部

通过文件创建RDD

  @Test
  //外部数据(文件)创建RDD
  def rddCreationFiles(): Unit = {
    //创建SparkConf
    val conf = new SparkConf().setMaster("local[6]").setAppName("rddCreationFiles")
    //创建SparkContext
    val sc = new SparkContext(conf)
    val rdd1: RDD[String] = sc.textFile("data/wordcount.txt")
    //使用通配符来读取
    val rdd2 = sc.textFile("data/word*.txt")
    rdd1.collect().foreach(println)
  }

注意点

  • path路径默认以当前环境的根路径为基准,可以写绝对路径,也可以写相对路径
  • 如果path路径使目录时,那么就代表读取多个文件
  • path可以使用通配符来读取

思考:当我们读取多个文件的时候,如何分辨数据是来自哪一个文件的?

    val rdd3 = sc.wholeTextFiles("data/wordcount.txt")
    rdd3.collect().foreach(println)

在这里插入图片描述
跳转顶部


从其他RDD衍生

    //1.创建SparkContext
    val conf = new SparkConf().setMaster("local[6]").setAppName("wordCount")
    val sc = new SparkContext(conf)

    //2.加载文件
    val rdd1: RDD[String] = sc.textFile("data/wordcount.txt")

    //3.处理
    //拆分
    val rdd2: RDD[String] = rdd1.flatMap(item => item.split(" "))
    //指定词频
    val rdd3: RDD[(String, Int)] = rdd2.map(item => (item, 1))
    //聚合
    val rdd4: RDD[(String, Int)] = rdd3.reduceByKey((curr, agg) => curr + agg)

    //4.得到结果
    val result: Array[(String, Int)] = rdd4.collect()
    result.foreach(item => println(item))

RDD的并行度和分区

  @Test
  def partitionAndParallelize():Unit = {
    //创建SparkConf
    val conf = new SparkConf()
      .setMaster("local[6]")
      .setAppName("partitionAndParallelize")
      .set("spark.default.parallelism","5")//设置默认分区数
    //创建SparkContext
    val sc = new SparkContext(conf)
    //makeRDD的第二个参数可以不传递,不传递就会使用默认值(默认值为当前最处理器的最大核数)
    val rdd = sc.makeRDD(
      List(1,2,3,4)
    )
    //将数据保存为分区文件
    rdd.saveAsTextFile("output")
  }

注意点

  • makeRDD的第二个参数可以不传递,不传递就会使用默认值(默认值为当前最处理器的最大核数)

数据分配的规律

def positions(length: Long, numSlices: Int): Iterator[(Int, Int)] = {(0 until numSlices).iterator.map { i =>
	 val start = ((i * length) / numSlices).toInt
	 val end = (((i + 1) * length) / numSlices).toInt(start, end)
	 }
}

跳转顶部


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值