1. 什么是RDD ?
弹性分布式数据集(Resilient Distributed Dataset,RDD),就是分布式的元素集合.
在 Spark 中,对数据的所有操作不外乎创建 RDD、转化已有 RDD 以及调用 RDD 操作进行求值
1.1 RDD基础
两种方法创建 RDD:
1. 读取一个外部数据集;
2. 在驱动器程序里分发驱动器程序中的对象集合(比如 list 和 set)。
示例:
// 在 Python 中使用 textFile() 创建一个字符串的 RDD
>>> lines = sc.textFile("README.md")
RDD 支 持 两 种 类 型 的 操 作:
1 . 转 化 操 作(transformation).
转化操作会由一个 RDD 生成一个新的 RDD.
// 调用转化操作 filter()
>>> pythonLines = lines.filter(lambda line: "Python" in line)
2 . 行 动 操 作(action)
行动操作会对 RDD 计算出一个结果,并把结果返回到驱动器程序中,或把结果存储到外部存储系统(如 HDFS)中.
// 调用 first() 行动操作
>>> pythonLines.first()
u'## Interactive Python Shell'
在实际操作中,你会经常用 persist() 来把数据的一部分读取到内存中,并反复查询这部分数据.
// 把 RDD 持久化到内存中
>>> pythonLines.persist
>>> pythonLines.count()
2
>>> pythonLines.first()
u'## Interactive Python Shell'
总的来说,每个 Spark 程序或 shell 会话都按如下方式工作。
(1) 从外部数据创建出输入 RDD。
(2) 使用诸如 filter() 这样的转化操作对 RDD 进行转化,以定义新的 RDD。
(3) 告诉 Spark 对需要被重用的中间结果 RDD 执行 persist() 操作。
(4) 使用行动操作(例如 count() 和 first() 等)来触发一次并行计算, Spark 会对计算进行优化后再执行。
2. 创建RDD
Spark 提供了两种创建 RDD 的方式:读取外部数据集,以及在驱动器程序中对一个集合进行并行化.
一种用得少的方法:
在学习Spark时候,在 shell 中快速创建出自己的 RDD, 然后对这些 RDD 进行操作
// Python 中的 parallelize() 方法
lines = sc.parallelize(["pandas", "i like pandas"])
另一种常见方法:
外部存储中读取数据来创建 RDD。
// Python 中的 textFile() 方法
lines = sc.textFile("/path/to/README.md")
3. RDD操作
RDD 支持两种操作: 转化操作和行动操作。
RDD 的转化操作是返回一个新的 RDD 的操作,比如 map() 和 filter(),而行动操作则是向驱动器程序返回结果或把结果写入外部系统的操作, 会触发实际的计算,比如 count() 和 first()。
举个例子,假定我们有一个日志文件 log.txt, 内含有若干消息,希望选出其中的错误消息。我们可以使用前面说过的转化操作 filter()。
// 用 Python 实现 filter() 转化操作
inputRDD = sc.textFile("log.txt")
errorsRDD = inputRDD.filter(lambda x: "error" in x)
再从 inputRDD 中找出所有包含单词 warning 的行。使用另一个转化操作 union() 来打印出包含 error 或 warning 的行数。
// Python 进行 union() 转化操作
errorsRDD = inputRDD.filter(lambda x: "error" in x)
warningsRDD = inputRDD.filter(lambda x: "warning" in x)
badLinesRDD = errorsRDD.union(warningsRDD)
3.1 转化操作
RDD 的转化操作是返回新 RDD 的操作.
此类操作有:
union()
// 用 Python 实现 filter() 转化操作
inputRDD = sc.textFile("log.txt")
errorsRDD = inputRDD.filter(lambda x: "error" in x)
// 用 Python 进行 union() 转化操作, 继续使用inputRDD
errorsRDD = inputRDD.filter(lambda x: "error" in x)
warningsRDD = inputRDD.filter(lambda x: "warning" in x)
badLinesRDD = errorsRDD.union(warningsRDD) // 合并
3.2 行动操作
行动操作是第二种类型的 RDD 操作,它们会把最终求得的结果返回到驱动器程序, 或者写入外部存储系统中。
由于行动操作需要生成实际的输出,它们会强制执行那些求值必须用到的 RDD 的转化操作。
继续日志的例子,我们可能想输出关于 badLinesRDD 的一些信息。
为此,需要使用两个行动操作来实现:
用 count() 来返回计数结果;
用 take() 来收集RDD 中的一些元素.
// 在 Python 中使用行动操作对错误进行计数
print "Input had " + badLinesRDD.count() + " concerning lines"
print "Here are 10 examples:"
for line in badLinesRDD.take(10):
print line
注意的是, 每当我们调用一个新的行动操作时,整个 RDD 都会从头开始计算。要避免这种低效的行为,用户可以将中间结果持久化.
3.3 惰性求值
(概念性理解即可)
RDD 的转化操作都是惰性求值的。这意味着在被调用行动操作之前 Spark 不会开始计算。Spark 使用惰性求值,这样就可以把一些操作合并到一起来减少计算数据的步骤。
Spark 使用惰性求值,这样就可以把一些操作合并到一起来减少计算数据的步骤。
4. 向Spark传递函数
这里依旧以Python代码示范.
word = rdd.filter(lambda s: "error" in s)
def containsError(s):
return "error" in s
word = rdd.filter(containsError)
5. 常见的转化操作和行动操作
5.1 基本RDD
首先来讲讲哪些转化操作和行动操作受任意数据类型的 RDD 支持。
针对各个元素的转化操作
map() 的返回值类型不需要和输入类型一样。
flatMap() 对每个输入元素生成多个输出元素.一个简单用途是把输入的字符串切分为单词.
distinct() 去重
sample(withReplacement, fraction, [seed]) 对 RDD 采样,以及是否替换伪集合操作
最简单的集合操作是 union(other),它会返回一个包含两个 RDD 中所有元素的 RDD。intersection(other) 方法,只返回两个 RDD 中都有的元素。
subtract(other) 函数接收另一个 RDD 作为参数,返回
一个由只存在于第一个 RDD 中而不存在于第二个 RDD 中的所有元素组成的 RDD。
cartesian() 与另一个 RDD 的笛卡儿积行动操作
最常见的行动操作 reduce(),接收一个函数作为参数,这个函数要操作两个 RDD 的元素类型的数据并返回一个同样类型的新元素。
// Python 中的 reduce()
sum = rdd.reduce(lambda x, y: x + y)
fold() 和 reduce() 类似,接收一个与 reduce() 接收的函数签名相同的函数,再加上一个”初始值”来作为每个分区第一次调用时的结果。
5.2 在不同RDD类型间转换
有些函数只能用于特定类型的 RDD.
Python 的 API 结构与 Java 和 Scala 有所不同。在 Python 中,所有的函数都实现在基本的RDD 类中,但如果操作对应的 RDD 数据类型不正确,就会导致运行时错误。