Spark算子API解析

本文详细介绍了Spark中的RDD算子,包括行动算子和转换算子,强调了依赖关系(窄依赖和宽依赖)、缓存策略(如checkpoint)以及RDD的分区器。此外,还讨论了RDD的创建、数据读取与保存以及累加器和广播变量的使用。
摘要由CSDN通过智能技术生成

首先要说明RDD的算子一共分为两种一种为行动算子一种为transformations算子。

依赖

RDDs通过操作算子进行转换,转换得到的新RDD包含了从其他RDDs衍生所必需的信息,RDDs之间维护着这种血缘关系,也称之为依赖。如下图所示,依赖包括两种,一种是窄依赖,RDDs之间分区是一一对应的,另一种是宽依赖,下游RDD的每个分区与上游RDD(也称之为父RDD)的每个分区都有关,是多对多的关系。

缓存

如果在应用程序中多次使用同一个RDD,可以将该RDD缓存起来,该RDD只有在第一次计算的时候会根据血缘关系得到分区的数据,在后续其他地方用到该RDD的时候,会直接从缓存处取而不用再根据血缘关系计算,这样就加速后期的重用。

CheckPoint

虽然RDD的血缘关系天然地可以实现容错,当RDD的某个分区数据失败或丢失,可以通过血缘关系重建。但是对于长时间迭代型应用来说,随着迭代的进行,RDDs之间的血缘关系会越来越长,一旦在后续迭代过程中出错,则需要通过非常长的血缘关系去重建,势必影响性能。为此,RDD支持checkpoint将数据保存到持久化的存储中,这样就可以切断之前的血缘关系,因为checkpoint后的RDD不需要知道它的父RDDs了,它可以从checkpoint处拿到数据。

RDD编程

RDD的创建:在Spark中创建RDD的创建方式可以分为三种:从集合中创建RDD;从外部存储创建RDD;从其他RDD创建。
1、从集合创建:主要提供了两个函数:parallelize和makeRDD
2、由外部存储系统的数据集创建包括本地的文件系统,还有所有Hadoop支持的数据集,比如HDFS、Cassandra、HBase等,比如:sc.textFile(“”url“”)具体含义后边讲解
3、从其他RDD转换

RDD的转换(面试开发重点)API

首先其整体分为Value型和Key、Value型

Value型:

函数 参数说明 举例
map 略,在Scala一文中已经说过
mapPartitions(func) 是对每一个分区进行一次map,假设有N个元素M个分区,那么map会执行N次而mapPatition则执行M次,因此其func的类型为Iterator【T】 mapPartition
mapPartitionsWithIndex(func) 这个就是指定某个索引值执行func,就是比上一个多了个可以指定某个分区进行func计算 mapPartitionsWithIndex(func)
flatMap(func)
glom 将每个分区变成数组形成新的RDD类型RDD[Array【T】 glom
groupBy(func) 将函数返回值相同的放入一个iterator中 groupBy
filter(func)
sample(withReplacement, fraction, seed) 抽样,第一参数为true表示放回的抽取,反之不放回抽取,第二个参数为抽样抽几个,第三个参数为随机数种子 sample
distinct([numTasks])) 对Rdd进行去重操作,可以传参数来指定有几个线程来完成 rdd.distinct(2)
coalesce(numPartitions) 缩减分区,由于大数据集过滤后,提高小数据执行效率 rdd.coalesce(3)
repartition(numPartitions) 根据分区数,重新通过网络随机洗牌所有数据 他和上面的coalesce的区别
sortBy(func,[ascending], [numTasks]) 将rdd用func进行排序,第二个为升序降序,第三个并行数 sortBy
pipe(command, [envVars]) 管道,针对每个分区,都执行一个shell角标,返回输出的RDD pipe

举例:
1、mapPartition:

var sc=new SparkContext();
var rdd=sc.parallelize(Array(1,2,3,4))
rdd.mapPartition(x=>x.map(_*2))

2、mapPartitionsWithIndex(func):
创建一个RDD,使每个元素跟所在分区形成一个元组组成一个新的RDD

var sc=new SparkContext();
var rdd=sc.parallelize(Array(1,2,3,4))
rdd.mapPartitionsWithIndex((i,its)=>its.map((_,i)))

map()和mapPartition()的区别:

  1. map():每次处理一条数据。
  2. mapPartition():每次处理一个分区的数据,这个分区的数据处理完后,原RDD中分区的数据才能释放,可能导致OOM。
  3. 开发指导:当内存空间较大的时候建议使用mapPartition(),以提高处理效率。

3、glom
创建一个4个分区的RDD,并将每个分区的数据放到一个数组

var sc=new SparkContext();
var rdd=sc.parallelize(1 to 16,4)
rdd.glom().collect()

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

var sc=new SparkContext();
var rdd=sc.parallelize(1 to 16,4)
rdd.groupBy(i=>i%2)

5、sample
需求:创建一个RDD(1-10),从中选择放回和不放回抽样

var sc=new SparkContext();
var rdd=sc.parallelize(1 to 16,4)
rdd.sample(true,0.4,2

6、coalesce和repartition的区别

  1. coalesce重新分区,可以选择是否进行shuffle过程。由参数shuffle: Boolean = false/true决定。
  2. repartition实际上是调用的coalesce,默认是进行shuffle的。源码如下:

7、sortBy:
按照与3余数的大小排序

var sc=new SparkContext();
var rdd=sc.parallelize(1 to 16,4)
rdd.sortBy(x=>x%3).collect()


输出结果为:res12: Array[Int] = Array(3, 4, 1, 2)

8、pipe
编写一个脚本,使用管道将脚本作用于RDD上。编写一个脚本,使用管道将脚本作用于RDD上。

编写一个脚本

Shell脚本
#!/bin/sh
echo "AA"
while read LINE; do
   echo ">>>"${LINE}
val rdd = sc.parallelize(List("hi","Hello","how","are","you"),1)
rdd.pipe("/opt/module/spark/pipe.sh").collect()

输出结果:
Array(AA, >>>hi, >>>Hello, >>>how, >>>are, >>>you)

双Value类型交互

函数 说明 举例
union(otherDataset) 对源RDD和参数RDD求并集后返回一个新的RDD union
subtract (otherDataset) 求差的一个函数 subtract
intersection(otherDataset) 求交集 intersection(otherDataset)
cartesian(otherDataset) 求笛卡尔积两个RDD的
zip(otherDataset) 拉链,将两个RDD组合成Key/Value形式的RDD,这里默认两个RDD的partition数量以及元素数量都相同,否则会抛出异常。 zip

1、union
创建两个RDD,求并集

//创建第一个RDD
val rdd1 = sc.parallelize(1 to 5)
//创建第二个RDD
val rdd2 = sc.parallelize(5 to 10)
//计算两个RDD的并集
val rdd3 = rdd1.union(rdd2)

rdd3的结果为:Array(1, 2, 3, 4, 5, 5, 6, 7, 8, 9, 10)

2、subtract
计算差的一种函数,去除两个RDD中相同的元素,不同的RDD将保留下来

val rdd = sc.parallelize(3 to 8)
val rdd1 = sc.parallelize(1 to 5)
rdd.subtract(rdd1).collect()

输出结果为: Array(8, 6, 7)

3、intersection(otherDataset)

val rdd1 = sc.parallelize(1 to 7)
 val rdd2 = sc.parallelize(5 to 10)
 val rdd3 = rdd1.intersection(rdd2)
 rdd3.collect()

输出结果为:Array(5, 6, 7)

4、zip

val rdd1 = sc.parallelize(Array(1,2,3),3)
val rdd2 = sc.parallelize(Array("a","b","c"),3)
rdd1.zip(rdd2).collect

输出结果为:Array((1,a), (2,b), (3,c))

Key-Value类型

函数 参数说明 举例
partitionBy 对RDD进行重新分区,如果原有的RDD和现有的RDD是一致的话就不进行分区,否则生成shuffleRDD,即会产生shuffle partitionBy
groupByKey 也是对每个key进行操作生成的序列(key,迭代器) groupByKey
reduceByKey(func, [numTasks]) func(x,y)其中x,y就是相同的key所对应的两个value比如(1,x),(1,y) reduceByKey
aggregateByKey zeroValue:给每一个分区中的每一个key一个初始值;seqOp:函数用于在每一个分区中用初始值逐步迭代value,可以理解为就是对每个分区执行的操作;)combOp:函数用于合并每个分区中的结果。 aggregateByKey
foldByKey 是上面的简化版其中第二个参数和第三个参数一致了 foldByKey
combineByKey[C] 其实他和aggregateByKey十分类似,只不过他的第一个参数可以对初始值进行改造 combineByKey
sortByKey([ascending], [numTasks]) 在一个(K,V)的RDD上调用,K必须实现Ordered接口,返回一个按照key进行排序的(K,V)的RDD sortByKey
mapValues 只针对于value进行操作 mapvalue
join(otherDataset, [numTasks]) 比如原始结果为a1(1,5),a2(1,9)那么进行join就变成了(1,(5,9)) join
cogroup(otherDataset, [numTasks]) 其实比较类似于上面函数,只不过返回的不是元组而是迭代器 cogroup

1、partitionBy
需求:创建一个4个分区的RDD,对其重新分区

val rdd = sc.parallelize(Array((1,"aaa"),(2,"bbb"),(3,"ccc"),(4,"ddd")),4)
var rdd2 = rdd.partitionBy(new org.apache.spark.HashPartitioner(2))

2、groupByKey
创建一个pairRDD,将相同key对应值聚合到一个sequence中,并计算相同key对应值的相加结果。

val words = Array("one", "two", "two", "three", "three", "three")
 val wordPairsRDD = sc.parallelize(words).map(word => (word, 1))
val group =  wordPairsRDD .groupByKey()
group.collect()

输出结果为: Array((two,CompactBuffer(1, 1)), (one,CompactBuffer(1)), (three,CompactBuffer(1, 1, 1)))

val res2=group.map((k,v)=>(k,v.sum))
res2.collect()

输出结果:Array[(String, Int)] 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值