Spark RDD总结

Spark RDD 总结

2019年11月21日
16:58

RDD两种操作

RDD 支持两种类型的操作:转化操作(transformation)和行动操作(action)

转化操作会由一个RDD生产一个新的RDD。 如fliter,map
行动操作会对RDD计算出一个结果。如first,count
两种操作区别在于Spark计算RDD的方式不同。Spark惰性计算,只有第一次在一个行动操作中用到时,才会真正计算。

RDD.persist()可以将RDD缓存下来,在多个行动操作中重用同一个RDD。

创建RDD,2种方式

  1. 读取外部数据集 (最常用的方式)
val lines = sc.textFile("/path/to/README.md")
  1. 在驱动器程序中对一个集合进行并行化。 将一个已有的集合传给SparkContext的parallelize()方法(除了开发原型和测试,这种方式用的不多)
val lines = sc.parallelize(List("pandas", "i like pandas"))

RDD操作

转化操作返回的是RDD,而行动操作返回的是其他的数据类型

errorsRDD = inputRDD.filter(lambda x: "error" in x)
warningsRDD = inputRDD.filter(lambda x: "warning" in x)
badLinesRDD = errorsRDD.union(warningsRDD)

以union()为例,转换操作可以操作任意数量的输入RDD

Spark会用谱系图(lineage graph)来记录这些不同RDD之间的依赖关系。
在这里插入图片描述
3-16:在Scala 中使用行动操作进行计数

println("Input had " + badLinesRDD.count() + " concerning lines")
println("Here are 10 examples:")
badLinesRDD.take(10).foreach(println)

collect函数不能用在大规模数据集上。单台机器内存能放下数据时使用。

最好把RDD看做我们通过转化操作构建出来的、记录如何计算数据的指令列表。读取数据操作也是惰性的。

向Spark传递函数

Python

注意:Python会在你不经意间吧函数所在的对象也序列化传出去。
正确做法:
class WordFunctions(object):

def getMatchesNoReference(self, rdd):
 #安全:只把需要的字段提取到局部变量中
query = self.query
return rdd.filter(lambda x: query in x)
 

Scala

所传递的函数及其引用的数据需要是可序列化的(实现了JAVA的Serializable接口)

Scala示例

class SearchFunctions(val query: String) {
def isMatch(s: String): Boolean = {
s.contains(query)
}
def getMatchesFunctionReference(rdd: RDD[String]): RDD[String] = {
// 问题:"isMatch"表示"this.isMatch",因此我们需要传递整个"this"
rdd.map(isMatch)
}
def getMatchesFieldReference(rdd: RDD[String]): RDD[String] = {
// 问题:"query"表示"this.query",因此我们需要传递整个"this"
rdd.map(x => x.split(query))
}
def getMatchesNoReference(rdd: RDD[String]): RDD[String] = {
// 安全:只把我们需要的字字段拿出来放入局部变量中
val query_ = this.query
rdd.map(x => x.split(query_))
}
}
 

常见的转化操作和行动操作

  1. 针对各个元素的转化操作
    map()和filter()

map()的输入RDD和输出RDD可以不是同一类型
flatMap()将行数据切分为单词

val lines = sc.parallelize(List("hello world", "hi"))
val words = lines.flatMap(line => line.split(" "))
words.first() // 􀝓􀣮"hello"
 

在这里插入图片描述
2. 伪集合操作
RDD支持许多数学上的集合操作,要求操作的RDD数据类型相同。

RDD.distinct() 返回只包含不同元素的新RDD,开销比较大
在这里插入图片描述
union中可包含重复数据。
intersection不包含重复数据,性能较差,因为需要通过网络混洗数据。
subtract和intersection一样,也需要数据混洗。
在这里插入图片描述
cartesian(other)求两个RDD的笛卡尔积,使用场景:在求用户相似度的应用,考虑所有可能的组合的相似度等应用。
注意:求大规模RDD的笛卡尔积开销巨大。
在这里插入图片描述
在这里插入图片描述

  1. 行动操作
reduce()
val sum = rdd.reduce((x, y) => x + y)
 
aggregate()
#计算RDD的平均值
val result = input.aggregate((0, 0))(
(acc, value) => (acc._1 + value, acc._2 + 1),
(acc1, acc2) => (acc1._1 + acc2._1, acc1._2 + acc2._2))
val avg = result._1 / result._2.toDouble

RDD的一些行动操作会以普通集合或值的形式将RDD的全部或部分数据返回驱动器程序中。
如collect(),通常在单元测试中使用
take(n)返回RDD中的n个元素,而且尝试只访问尽量少的分区,返回顺序不确定
top()会使用数据的默认顺序
takeSample(withReplacement,num,seed)函数,从数据中获取一个采样,并指定是否替换。
foreach()对RDD的所有元素应用一个行动操作,但不把任何结果返回到驱动器程序中。
在这里插入图片描述

  1. 在不同RDD类型间转换
    有些函数只能用于特定类型的RDD,如mean()和variance()只能用在数值RDD上。Join()只能用在键值对RDD上。

在Scala中,将RDD转为有特定函数的RDD(如在RDD[Double]上进行数值操作)是由隐式转换来自动处理的。
需要加上import org.apache.spark.SparkContext._来使用这些隐式转换。例如隐式转换可以把RDD[Double]转为DoublleRDDFunctions.查文档时不要忘记这些封装的专用类中的函数。

持久化(缓存)

例子:Scala中的两次执行

val result = input.map(x => x*x)
println(result.count())
println(result.collect().mkString(","))

为了避免多次计算同一个RDD,可以让Spark对数据进行持久化。

默认情况下persist()会把数据以序列化的形式缓存在JVM的堆空间中。

在这里插入图片描述

在Scala中使用persist()

val result = input.map(x => x * x)
result.persist(StorageLevel.DISK_ONLY)
println(result.count())
println(result.collect().mkString(","))
 

RDD 还有一个方法叫做unpersist(),调用该方法可以手动把持久化的RDD从缓存中移除。

<think>好的,用户想了解Spark RDD的使用和概念。首先我需要回顾一下RDD的基本定义和核心特性。根据引用[3],RDD是弹性分布式数据集的缩写,是Spark对不可变分布式数据集的抽象。这意味着RDD代表一个可以并行操作的元素集合,具有容错性。 接下来,我需要解释RDD的五大特性,包括分区、依赖关系、计算函数、分区器以及优先位置。这些都是RDD的核心概念,用户需要清楚这些特性如何支持容错和高效计算。 然后要讲RDD的创建方式。常见的有从外部数据源加载,比如HDFS或本地文件系统,或者通过转换已有的RDD生成新的RDD。需要举一些例子,比如parallelize方法或者textFile方法。 关于RDD的操作,分为转换(Transformations)和动作(Actions)。转换是惰性的,只有触发动作时才会执行。这里可以举一些常用的转换和动作的例子,如map、filter、reduceByKey,以及collect、count等。同时要注意区分宽依赖和窄依赖,宽依赖会触发shuffle,影响性能。 容错机制方面,RDD通过血统(Lineage)来恢复丢失的分区。每个RDD记录了自己是如何从其他RDD转换而来的,这样在节点失败时可以根据这些信息重新计算丢失的数据,而不需要复制多份数据。 另外,用户可能对RDDSpark生态系统中的位置感兴趣。比如,DStream是Spark Streaming的基础,由一系列RDD组成,如引用[1]和[2]提到的。而结构化流(Structured Streaming)则是基于DataFrame,优化更多,但底层可能还是利用了RDD的特性。 需要提到RDD的适用场景,比如需要低延迟操作、精细控制数据分区,或者使用非结构化数据时。同时也要指出当需要更高级优化时,DataFrame或Dataset可能更合适,如引用[5]提到的转换器和预测器的例子,虽然那更多是ML库的内容,但能说明生态系统的多样性。 最后,生成的相关问题需要覆盖RDD与其他组件的比较,比如DStream和DataFrame,以及优化方法、容错机制和适用场景。这样用户如果有进一步的问题,可以继续深入。</think>### Spark RDD 核心概念与使用详解 #### 一、RDD 基础定义 **弹性分布式数据集(Resilient Distributed Dataset, RDD)** 是 Spark 的底层核心抽象,代表一个**不可变、分区的数据集合**,支持并行操作[^3]。其核心特性可总结为五大要素: - **分区列表**:数据被划分为多个分区(Partition),每个分区在集群节点上分布式存储 - **依赖关系**:记录父 RDD 到子 RDD 的转换关系(窄依赖/宽依赖) - **计算函数**:用于从父 RDD 计算当前分区的函数 - **分区器**:定义数据分布规则(如 HashPartitioner) - **优先位置列表**:标识数据分片的最佳计算位置(数据本地性优化) #### 二、RDD 创建方式 1. **从外部数据源加载**: ```python # 从本地文件创建 rdd = sc.textFile("hdfs://path/to/file.txt") # 从集合创建 rdd = sc.parallelize([1,2,3,4,5]) ``` 2. **通过转换操作生成**: ```python mapped_rdd = rdd.map(lambda x: x*2) filtered_rdd = rdd.filter(lambda x: x>3) ``` #### 三、核心操作类型 | 操作类型 | 特点 | 示例 | |---------|------|------| | **Transformations**(转换) | 惰性执行,生成新 RDD | `map()`, `filter()`, `groupByKey()` | | **Actions**(动作) | 触发实际计算 | `collect()`, `count()`, `saveAsTextFile()` | | **宽依赖操作** | 涉及数据重分布(Shuffle) | `join()`, `reduceByKey()` | #### 四、容错机制 通过**血统(Lineage)**实现容错: $$ \text{RDD}_n = f(\text{RDD}_{n-1}) = f_{n} \circ f_{n-1} \circ \cdots \circ f_1(\text{原始数据}) $$ 当某个分区丢失时,Spark 可根据这个依赖链重新计算该分区[^3]。 #### 五、应用模式示例 ```python # 词频统计经典案例 lines = sc.textFile("hdfs://...") words = lines.flatMap(lambda line: line.split(" ")) pairs = words.map(lambda word: (word, 1)) counts = pairs.reduceByKey(lambda a,b: a+b) counts.saveAsTextFile("hdfs://output") ``` #### 六、与流处理的关联 在 Spark Streaming 中,DStream 本质上是**时间维度上的 RDD 序列**: $$ \text{DStream} = \{ \text{RDD}_t \mid t \in \text{时间窗口} \} $$ 每个微批处理实际上是对 RDD 的操作[^1][^2]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值