Day67_Spark(二)Spark RDD操作

课程大纲

课程内容

学习效果

掌握目标

Spark执行流程

Wordcount执行流程

掌握

Spark作业提交流程

掌握

RDD操作

RDD初始化

掌握

RDD操作

掌握

变量

掌握

排序

高级排序

掌握

一、Spark执行流程

​    在上一讲中,我们知道了什么是Spark,什么是RDD、Spark的核心构成组件,以及Spark案例程序。在这一讲中,我们将继续需要Spark作业的执行过程,以及编程模型RDD的各种花式操作,首先来学习Spark作业执行流程。

(一)、WordCount执行流程

1、WordCount执行流程图

Wordcount执行流程入图2-1所示。

2、归纳总结

在上图中我们可以看到rdd(partition)和rdd之间是有依赖关系的,大致分为两种:窄依赖(Narrow Dependency)和宽依赖(Wide/Shuffle Dependency)。

  • 窄依赖:rdd中partition中的数据,只依赖于父rdd中的一个分区,把这种依赖关系,称之为窄依赖,常见的窄依赖操作有:flatMap、map、filter、union、coalesce等等。
  • 宽依赖:与窄依赖对应的,partition中的数据,依赖于父rdd中所有的partition,把这种依赖关系称之为宽依赖,常见的宽依赖操作:distinct、reduceByKey、groupByKey、repartition等等。

总而言之,rdd和rdd是有依赖关系的,我们把rdd和rdd之间关系构成的一个图或者依赖的链条,称之为为rdd的lineage(血统),是保障spark容错的一个重要支撑。

(二)、Spark作业提交流程

下面我们一同来看spark作业提交流程,流程如图2-2所示。

二、RDD的基本操作

(一)、RDD概述

在较高的层次上,每个Spark应用程序都由一个驱动程序组成,该驱动程序运行用户的主功能并在集群上执行各种并行操作。Spark提供的主要抽象是一个弹性分布式数据集(RDD),它是一个跨集群节点划分的元素集合,可以并行操作。RDD是从Hadoop文件系统(或任何其他支持Hadoop的文件系统)中的一个文件或驱动程序中现有的Scala集合开始创建的,并对其进行转换。用户还可以要求Spark将RDD持久化在内存中,这样就可以跨并行操作高效地重用RDD。最后,RDD会自动从节点故障中恢复。

1、RDD操作算子分类

需要知道RDD操作算子的分类,基本上分为两类:transformation和action,当然更加细致的分,可以分为输入算子,转换算子,缓存算子,行动算子,整个RDD原生数据空间如下图2-3所示。

  • 输入:在Spark程序运行中,数据从外部数据空间(如分布式存储:textFile读取HDFS等,parallelize方法输入Scala集合或数据)输入Spark,数据进入Spark运行时数据空间,转化为Spark中的数据块,通过BlockManager进行管理。
  • 运行:在Spark数据输入形成RDD后便可以通过转换算子,如filter等,对数据进行操作并将RDD转化为新的RDD,通过Action算子,触发Spark提交作业。 如果数据需要复用,可以通过Cache算子,将数据缓存到内存。
  • 输出:程序运行结束数据会输出Spark运行时空间,存储到分布式存储中(如saveAsTextFile输出到HDFS),或Scala数据或集合中(collect输出到Scala集合,count返回Scala int型数据)。

2、RDD初始化

RDD的初始化,原生api提供的2中创建方式,一种就是读取文件textFile,还有一种就是加载一个scala集合parallelize。当然,也可以通过transformation算子来创建的RDD。

(二)、RDD操作

(1)map算子:

说明

rdd.map(p: A => B):RDD,对rdd集合中的每一个元素,都作用一次该func函数,之后返回值为生成元素构成的一个新的RDD。

总结:map操作是一个one-2-one的操作。

编码

对rdd中的每一个元素×7

        val sc = new SparkContext(conf)
        //map 原集合*7
        val list = 1 to 7
        //构建一个rdd
        val listRDD:RDD[Int] = sc.parallelize(list)
//        listRDD.map((num:Int) => num * 7)
//        listRDD.map(num => num * 7)
        val ret = listRDD.map(_ * 7)
        ret.foreach(println)

(2)flatMap算子:

a. 说明

rdd.flatMap(p: A => 集合):RDD ==>rdd集合中的每一个元素,都要作用func函数,返回0到多个新的元素,这些新的元素共同构成一个新的RDD。所以和上述map算子进行总结:

flatMap操作是一个one-2-many的操作

b. 编码

案例:将每行字符串,拆分成一个个的单词

def  flatMapOps(sc:SparkContext): Unit = {
    val list = List(
        "jia jing kan kan kan",
        "gao di di  di di",
        "zhan yuan qi qi"
    )
    val listRDD = sc.parallelize(list)
    listRDD.flatMap(line => line.split("\\s+"))
    .foreach(println)
}

(3)mapPartitions算子:

a.说明

mapPartitions(p: Iterator[A] => Iterator[B]),上面的map操作,一次处理一条记录;而mapPartitions一次性处理一个partition分区中的数据。

注意:虽说mapPartitions的执行性能要高于map,但是其一次性将一个分区的数据加载到执行内存空间,如果该分区数据集比较大,存在OOM的风险

b. 编码

//创建RDD并指定分区数
val rdd: RDD[Int] = sc.parallelize(Array(1,2,3,4),2)
//通过-将分区之间的数据连接
val result: RDD[String] = rdd.mapPartitions(x=>Iterator(x.mkString("-")))
//打印输出
println(result.collect().toBuffer)

(4)mapPartitionsWithIndex算子:

a.说明

mapPartitionsWithIndex((index, p: Iterator[A] => Iterator[B])),该操作比mapPartitions多了一个index,代表就是后面p所对应的分区编号。

rdd的分区编号,命名规范,如果有N个分区,分区编号就从0,...,N-1。

b. 编码

val rdd: RDD[Int] = sc.parallelize(1 to 16,4)
//查看每个分区当中都保存了哪些数据
val result: RDD[String] = rdd.mapPartitionsWithIndex((index,item)=>Iterator(index+":"+item.mkString(",")))
//打印输出
result.foreach(println)

(5)sample算子:

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值