spark-RDD编程 持久化,常用算子总结

spark-RDD编程 持久化,常用算子总结

RDD编程

RDD基础

RDD:弹性分布式数据集(Resilient Distributed Dataset),是spark对数据的核心抽象,spark中RDD其实就是不可变的分布式的元素集合。

创建RDD一般有两种方式:1、读取外部数据集;2、其他RDD转换。

RDD创建出来后支持转换操作(transformation)和行动操作(action)两种操作。转换操作会把一个RDD生成一个新的RDD,行动操作会对RDD计算出一个结果。

RDD特点

RDD表示只读的分区的数据集,对RDD进行改动,只能通过RDD的转换操作,由一个RDD得到一个新的RDD,新的RDD包含了从其他RDD衍生所必需的信息。RDDs之间存在依赖,RDD的执行是按照血缘关系延时计算的。如果血缘关系较长,可以通过持久化RDD来切断血缘关系。

编程模型

在spark中,RDD被表示为对象,通过对象的方法对RDD进行转换。经过一系列转换之后通过行动算子触发计算返回结果。使用spark需要编写一个Driver程序,它被提交到集群以调度运行Worker,Driver中定义了一个或多个RDD,并调用RDD上的action,Worker则执行RDD分区计算任务。

在这里插入图片描述

创建RDD

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

Java创建RDD

SparkConf conf = new SparkConf().setMaster("local").setAppName("My App");
JavaSparkContext sc = new JavaSparkContext(conf);
//    从集合创建RDD
JavaRDD<String> lines_java = sc.parallelize(Arrays.asList("pandas", "i like pandas"));
//    是从外部存储中读取数据来创建 RDD
JavaRDD<String> lines_txt_java = sc.textFile("path");

Scala创建RDD

//    spark配置
val conf = new SparkConf().setAppName("spark").setMaster("local")
//    获取sparkContext
val sc = new SparkContext(conf)
//    从集合创建RDD
val lines = sc.parallelize(List("pandas", "i like pandas"))
//    是从外部存储中读取数据来创建 RDD
val lines_txt = sc.textFile("path")

RDD操作

转换

RDD的转换操作最终生成一个新的RDD,转换出来的RDD是惰性求值的。

行动

每当我们调用一个新的行动操作时,整个 RDD 都会从头开始计算。可以通过将中间结果持久化来避免这种低效的行为。

惰性求值

RDD的转换都是惰性求值的,在行动算子之前spark不会进行计算。

常见的算子(基础RDD,针对各元素的算子)

转换算子
map(func)

对rdd中数据进行计算后得到新RDD。将原数据的每个元素传给函数func进行格式化,返回一个新的分布式数据集

val lines = sc.parallelize(List(1, 2, 3, 4))
val result = lines.map(x=>x*x)
result.collect().foreach(println)

返回

1
4
9
16
mapPartitions(func)

类似于map,但独立地在RDD的每一个分片上运行。假设有N个元素,有M个分区,那么map的函数的将被调用N次,而mapPartitions被调用M次,一个函数一次处理所有分区。

val lines = sc.parallelize(List(1, 2, 3, 4))
lines.mapPartitions(x=>x.map(x=>x*2)).collect().foreach(println)

返回

2
4
6
8
map()和mapPartition()的区别
  1. map():每次处理一条数据。

  2. mapPartition():每次处理一个分区的数据,这个分区的数据处理完后,原RDD中分区的数据才能释放,可能导致OOM。

当内存空间较大的时候建议使用mapPartition(),以提高处理效率。

glom

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

scala> val rdd = sc.parallelize(1 to 16,4)
rdd: org.apache.spark.rdd.RDD[Int] = ParallelCollectionRDD[0] at parallelize at <console>:24

scala> rdd.glom().collect()
res0: Array[Array[Int]] = Array(Array(1, 2, 3, 4), Array(5, 6, 7, 8), Array(9, 10, 11, 12), Array(13, 14, 15, 16))
groupBy(func)

按照传入函数的返回值进行分组。

scala> val rdd = sc.parallelize(1 to 4)
rdd: org.apache.spark.rdd.RDD[Int] = ParallelCollectionRDD[2] at parallelize at <console>:24

scala> val group = rdd.groupBy(_%2).collect
group: Array[(Int, Iterable[Int])] = Array((0,CompactBuffer(2, 4)), (1,CompactBuffer(1, 3)))
flatMap()

将函数应用于 RDD 中的每个元素,将返回的迭代器的所有内容构成新的 RDD。有着一对多的表现,输入一输出多。通常用来切分单词

val lines1 = sc.parallelize(List("hello world", "hi"))
val words = lines1.flatMap(line => line.split(" "))
words.collect().foreach(println)

返回

hello
world
hi
filter()

返回一个由通过传给 filter() 的函数的元素组成的 RDD。通过函数筛选出需要的数据元素,返回true表示保留,返回false表示抛弃。

val lines = sc.parallelize(List(1, 2, 3, 4))
lines.filter(x => x != 1).collect().foreach(println)

返回

2
3
4
distinct()

去重。distinct() 操作的开销很大,会有shuffle。

val lines2 = sc.parallelize(List(1, 2, 3, 4,4))
lines2.distinct().collect().foreach(println)

返回

4
1
3
2
union()

生成一个包含两个 RDD 中所有元素的 RDD。即求并集。如果输入的 RDD 中有重复数据,Spark 的 union() 操作也会包含这些重复数据。

val rdd = sc.parallelize(List(1,2,3))
val other = sc.parallelize(List(3,4,5))
rdd.union(other).collect().foreach(println)

返回

1
2
3
3
4
5
intersection()

求两个RDD 共同的元素的 RDD。即求交集。intersection() 在运行时也会去掉所有重复的元素(单个 RDD 内的重复元素也会一起移除),intersection()在运行时会产生shuffle,会产生大量开销。

val rdd = sc.parallelize(List(1,2,3))
val other = sc.parallelize(List(3,4,5))
rdd.intersection(other).collect().foreach(println)

返回

3
subtract()

移除一个RDD 中的内容。即求差集。subtract()在运行时会产生shuffle

val rdd = sc.parallelize(List(1,2,3))
val other = sc.parallelize(List(3,4,5))
rdd.subtract(other).collect().foreach(println)

返回

1
2
cartesian()

与另一个 RDD 的笛卡儿积。

val rdd = sc.parallelize(List(1,2,3))
val other = sc.parallelize(List(3,4,5))
rdd.cartesian(other).collect().foreach(println)

返回

(1,3)
(1,4)
(1,5)
(2,3)
(2,4)
(2,5)
(3,3)
(3,4)
(3,5)
行动算子
reduce(func)

RDD进行的聚合操作。接收一个函数作为参数,这个函数要操作两个 RDD 的元素类型的数据并返回一个同样类型的新元素。

val lines = sc.parallelize(List(1, 2, 3, 4))
println(lines.reduce((x,y)=>x+y))

返回

10
fold(value)(func)

fold()与reduce()类似,接收与reduce接收的函数签名相同的函数,另外再加上一个初始值作为第一次调用的结果。(例如,加d法初始c值应为0,乘法初始值应为1)

val rdd = sc.makeRDD(List("a","b","c","d"))
println(rdd.fold("A")((x, y) => x + y))
val rdd1 = sc.makeRDD(List("a","b","c","d"),2)
println(rdd1.fold("A")((x, y) => x + y))

返回

AAabcd
AAabAcd
aggregate(0, 0)(seqOp, combOp)

fold()和reduce()的返回值必须与rdd的数据类型相同,aggregate()函数打破了这个限制。

val lines = sc.parallelize(List(1, 2, 3, 4))
val res = lines.aggregate(0, 0)(((x, y) => (x._1 + y, x._2 + 1)), ((x, y) => (x._1 + y._1, x._2 + y._2)))
println(res._1/res._2.toDouble)
/**seqOp函数(x, y) => (x._1 + y, x._2 + 1) 其中x为返回值,y为lines中的元素遍历,返回一个(int,int)类型的返回值,x._1 + y计算所有元素和,x._2 + 1计算元素个数。
combOp函数(x, y) => (x._1 + y._1, x._2 + y._2) 由于是分区计算,该函数将各分区结果相加
res._1/res._2.toDouble计算出平均值
*/

返回

2.5
collect()

返回 RDD 中的所有元素

count()

RDD 中的元素个数

countByValue()

各元素在 RDD 中出现的次数

take(num)

从 RDD 中返回 num 个元素

top(num)

从 RDD 中返回最前面的 num 个元素

takeOrdered(num) (ordering)

从 RDD 中按照提供的顺序返 回最前面的 num 个元素

takeSample(withReplace ment, num, [seed])

从 RDD 中返回任意一些元素

foreach(func)

对 RDD 中的每个元素使用给定的函数

first()

返回数据集中的第一个元素

示例

val lines = sc.parallelize(List(1, 2, 3, 4))
println("countByValue算子返回")
println(lines.countByValue())
println("take算子返回")
println(lines.take(2).mkString(" "))
println("count算子返回")
println(lines.count())
println("top算子返回")
println(lines.top(2).mkString(" "))
println("takeOrdered算子默认排序返回")
lines.takeOrdered(3).foreach(println)
println("takeOrdered算子反转排序返回")
lines.takeOrdered(3)(Ordering[Int].reverse.on(x=>x)).foreach(println)
println("takeSample算子返回")
lines.takeSample(false,1).foreach(println)
println("first算子返回")
println(lines.first())

返回

countByValue算子返回
Map(4 -> 1, 1 -> 1, 3 -> 1, 2 -> 1)
take算子返回
1 2
count算子返回
4
top算子返回
4 3
takeOrdered算子默认排序返回
1
2
3
takeOrdered算子反转排序返回
4
3
2
takeSample算子返回
3
first算子返回
1

持久化

spark RDD是惰性求值的,很多时候我们想多次使用同一个RDD,如果直接调用行动算子,spark每次都会重新计算RDD以及它的所有依赖。为了避免多次计算同一个RDD,可以让spark对数据进行持久化。

spark持久化存储数据时,计算出RDD的节点会分别保存他们计算出的分区数据。如果某个节点发生故障,spark会在需要用到缓存数据的时候重新计算丢失的数据分区。如果希望节点故障的情况不会拖累我们的执行速度,也可以把数据备份到多个节点上。

org.apache.spark.storage.StorageLevel和pyspark.StorageLevel中的持久化级 别;如有必要,可以通过在存储级别的末尾加上“_2”来把持久化数据存为两份

级别使用的空间CPU时间是否在内存中是否在磁盘上备  注
MEMORY_ONLY
MEMORY_ONLY_SER
MEMORY_AND_DISK中等部分部分如果数据在内存中放不下,则溢写到磁 盘上
MEMORY_AND_DISK_SER部分部分如果数据在内存中放不下,则溢写到磁 盘上。在内存中存放序列化后的数据
DISK_ONLY
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值