Spark(二):Spark的RDD

本文详细介绍了Spark中的核心概念——弹性分布式数据集(RDD)。RDD具有只读、分区、依赖、缓存和检查点等五大特性,并探讨了其编程模型,包括创建RDD、转换算子和行动算子的使用。此外,还讲解了RDD的共享变量,如累加器和广播变量。通过对RDD的深入理解,开发者能够更好地掌握Spark并行计算的本质。
摘要由CSDN通过智能技术生成

Spark(二):Spark的RDD

Spark 程序,一般都包含一个Driver Program用于运行main函数,在该函数中执行着各种各样的并行操作。其中在Spark中有重要的概念RDD。该RDD是一个带有分区的分布式数据集,将数据分布存储在Spark集群的各个节点。当对RDD做任何操作,该操作都是并行的。

RDD特点

RDD Represents an immutable, partitioned collection of elements that can be operated on in parallel.
RDD 代表者一个不可变、带有分区的集合,可以被并行操作。

五大特性

- A list of partitions(带有分区)
- A function for computing each split(每个分区都是独立运行function操作的,继而实现并行)
- A list of dependencies on other RDDs(因为RDD是不可变的,因此RDD存在一种转换依赖关系,将这种转换依赖关系成为RDD的血统-lineage,可以实现RDD的故障恢复)
- Optionally, a Partitioner for key-value RDDs (e.g. to say that the RDD is hash-partitioned)(可以对Key-Value类型的数据,指定Partitioner策略,默认系统使用Hash-Partitioned)
- Optionally, a list of preferred locations to compute each split on (e.g. block locations for an HDFS file)(Spark在计算HDFS的时候,可以考虑最优计算策略。)

RDD编程

1、编程模型

在Spark中,RDD被表示为对象,通过对象上的方法调用来对RDD进行转换。经过一系列的transformations定义RDD之后,就可以调用actions触发RDD的计算,action可以是向应用程序返回结果(count, collect等),或者是向存储系统保存数据(saveAsTextFile等)。在Spark中,只有遇到action,才会执行RDD的计算(即延迟计算),这样在运行时可以通过管道的方式传输多个转换。

要使用Spark,开发者需要编写一个Driver程序,它被提交到集群以调度运行Worker,如下图所示。Driver中定义了一个或多个RDD,并调用RDD上的action,Worker则执行RDD分区计算任务。

在这里插入图片描述

2、RDD的创建

在Spark中创建RDD的创建方式可以分为三种:从集合中创建RDD;从外部存储创建RDD;从其他RDD创建。

① 从集合中创建RDD,Spark主要提供了两种函数:parallelize和makeRDD

1)使用parallelize()从集合创建

val rdd = sc.parallelize(Array(1,2,3,4,5,6,7,8))

2)使用makeRDD()从集合创建

val rdd1 = sc.makeRDD(Array(1,2,3,4,5,6,7,8))

makeRDD()底层依然调用了parallelize(),本质两种方式是一样的

② 由外部存储系统的数据集创建 【从hdfs,本地文件 读取数据创建RDD】

val rdd2 = sc.textFile("hdfs://spark1:9000/a.txt")

③ 从其他RDD创建(转换算子)

3、RDD的转换(Transformations)算子

Value类型

map(func)案例

作用:返回一个新的RDD,该RDD由每一个输入元素经过func函数转换后组成

需求:创建一个1-10数组的RDD,将所有元素*2形成新的RDD

//1.创建一个RDD
val rdd1: RDD[Int] = sc.makeRDD(1 to 10)
//2.将所有元素*2
val rdd2: RDD[Int] = rdd1.map(_*2)
//3.在驱动程序中,以数组的形式返回数据集的所有元素
val arr: Array[Int] = rdd2.collect()
//4.输出数组中的元素
arr.foreach(println)

mapPartitions(func)案例

作用:类似于map,但独立地在RDD的每一个分区上运行,因此在类型为T的RDD上运行时,func的函数类型必须是Iterator[T] => Iterator[U]。假设有N个元素,有M个分区,那么map的函数的将被调用N次,而mapPartitions被调用M次,一个函数一次处理所有分区。

需求:创建一个RDD,使每个元素*2组成新的RDD

//1.创建一个RDD
val rdd1: RDD[Int] = sc.makeRDD(1 to 10)
//2.将所有元素*2
val rdd2: RDD[Int] = rdd1.mapPartitions(iter => {
   iter.map(_ * 2)
})
//3.打印结果
rdd2.collect().foreach(println)

map和mapPartition的区别
map():每次处理一条数据。
mapPartition():每次处理一个分区的数据

mapPartitionsWithIndex(func)案例

作用:类似于mapPartitions,但func带有一个整数参数表示分区的索引值,因此在类型为T的RDD上运 行时,func的函数类型必须是(Int, Interator[T]) => Iterator[U];

需求:创建一个RDD,使每个元素跟所在分区形成一个元组组成一个新的RDD

//1.创建一个RDD
val rdd1: RDD[Int] = sc.makeRDD(1 to 10)
//2.使每个元素跟所在分区形成一个元组组成一个新的RDD
val rdd2: RDD[(Int, Int)] = rdd1.mapPartitionsWithIndex((index, iter) => 
					{
   
						iter.map((index, _))
						})
//3.打印新的RDD
rdd2.collect().foreach(println)

flatMap(func)案例

作用:类似于map,但是每一个输入元素可以被映射为0或多个输出元素(所以func应该返回一个序列,而不是单一元素)

需求:创建一个元素为1-5的RDD,运用flatMap创建一个新的RDD,新的RDD为(1,1, 2, 1, 2, 3, 1, 2, 3, 4, 1, 2, 3, 4, 5)

//1.创建一个RDD
   val rdd1: RDD[Int] = sc.makeRDD(1 to 5)
   //2.根据原RDD创建新RDD
   val rdd2 = rdd1.flatMap(v => 1 to v)
   //3.打印新的RDD
   rdd2.collect().foreach(v=>{
   
       print(v + " ")
   })

glom案例

作用:将每一个分区形成一个数组,形成新的RDD类型是RDD[Array[T]]

需求:创建一个4个分区的RDD,并将每个分区的数据放到一个数组

   //1.创建一个RDD
   val rdd1: RDD[Int] = sc.makeRDD(1 to 16,4)
   //2.根据原RDD创建新RDD
   val rdd2: RDD[Array[Int]] = rdd1.glom()
   //3.打印
   rdd2.collect().foreach(v=>{
   
       println(v.mkString(","))
   })
   //每个分区输出一个数组,保存当前分区数据

应用场景:计算RDD中的最大值

groupBy(func)案例
作用:分组,按照传入函数的返回值进行分组。将相同的key对应的值放入一个迭代器。

需求:创建一个RDD,按照元素模以2的值进行分组。

//1.创建一个RDD
 val rdd1: RDD[Int] = sc.makeRDD(1 to 10)
 //2.根据原RDD创建新RDD
 val rdd2: RDD[(Int, Iterable[Int])] = rdd1.groupBy(v=>v%2)
 //3.打印
 rdd2.collect().foreach(println)

filter(func)案例

作用:过滤。返回一个新的RDD,该RDD由经过func函数计算后返回值为true的输入元素组成。

需求:创建一个RDD(由字符串组成),过滤出一个新RDD(包含”xiao”子串)

//1.创建一个RDD
val rdd1: RDD[String] = sc.makeRDD(List("xiaoming","zhangsan","xiaohong","lisi","wangxiao"))
//2.根据原RDD创建新RDD
val rdd2: RDD[String] = rdd1.filter(v=>v.contains("xiao"))
//3.打印
rdd2.collect().foreach(println)

distinct案例

作用:对源RDD进行去重后返回一个新的RDD

需求:创建一个RDD,使用distinct()对其去重。

//1.创建一个RDD
val rdd1: RDD[Int] = sc.makeRDD(List(1,2,3,3,5,6,7,1,2,3))
//2.根据原有RDD去重,产生新的RDD
val rdd2: RDD[Int] = rdd1.distinct()
//3.打印
rdd2.collect().foreach(println)

coalesce(numPartitions)案例

作用:改变分区数,用于大数据集过滤后,提高小数据集的执行效率。

当改变后分区数小于原分区数,直接调用传入分区数即可。此时减少分区的原理是将多出来的分区随机合并,不触发shuffle。默认shuffle为false

当改变后分区数大于原分区数,需要当shuffle的值设置为true,调用传入分区数即可 rdd.coalesce(6,true)。此时增加分区的原理是重新洗牌,触发shuffle。

需求:创建一个4个分区的RDD,对其缩减分区

//1.创建一个RDD
   val rdd1: RDD[
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值