RDD
1.用户可以使用两种方法创建RDD:读取一个外部数据集,或在驱动器程序里分发驱动器程序中的对象集合(比如list 和set)。
1.1读取一个外部数据集
在本书前面的章节中已经见过使用SparkContext.textFile() 来读取文本文件作为一个字符串RDD 的示例.
lines = sc.textFile("README.md")
1.2在驱动器程序中对一个集合进行并行化。
创建RDD 最简单的方式就是把程序中一个已有的集合传给SparkContext 的parallelize()
方法。
除了开发原型和测试时,这种方式用得并不多,毕竟这种方式需要把你的整个数据集先放在一台机器的内存中。
Python 中的parallelize() 方法
lines = sc.parallelize(["pandas", "i like pandas"])
Scala 中的parallelize() 方法
val lines = sc.parallelize(List("pandas", "i like pandas"))
2.创建出来后,RDD 支持两种类型的操作: 转化操作(transformation) 和行动操作
(action)
RDD 的转化操作是返回一个新的RDD 的操作,比如map() 和filter(),而行动操作则是向驱动器程序返回结果或把结果写入外部系统的操作,会触发实际的计算,比如count() 和first()。
Spark 对待转化操作和行动操作的方式很不一样,因此理解你正在进行的操作的类型是很重要的。如果对于一个特定的函数是属于转化操作还是行动操作感到困惑,你可以看看它的返回值类型:转化操作返回的是RDD,而行动操作返回的是其他的数据类型。
2.1 转化操作会由一个RDD 生成一个新的RDD
# 用Python 实现filter() 转化操作
inputRDD = sc.textFile("log.txt")
errorsRDD = inputRDD.filter(lambda x: "error" in x)
# 用Scala 实现filter() 转化操作
val inputRDD = sc.textFile("log.txt")
val errorsRDD = inputRDD.filter(line => line.contains("error"))
filter() 操作不会改变已有的inputRDD 中的数据。实际上,该操作会返回一个全新的RDD。inputRDD 在后面的程序中还可以继续使用,比如我们还可以从中搜索别的单词。
事实上,要再从inputRDD 中找出所有包含单词warning 的行。
接下来,我们使用另一个转化操作union() 来打印出包含error 或warning 的行数。下例中用Python 作了示例,不过union() 函数的用法在所有三种语言中是一样的。
用Python 进行union() 转化操作
errorsRDD = inputRDD.filter(lambda x: "error" in x)
warningsRDD = inputRDD.filter(lambda x: "warning" in x)
badLinesRDD = errorsRDD.union(warningsRDD)
通过转化操作,你从已有的RDD 中派生出新的RDD,Spark 会使用谱系图(lineage graph)来记录这些不同RDD 之间的依赖关系。Spark 需要用这些信息来按需计算每个RDD,也可以依靠谱系图在持久化的RDD 丢失部分数据时恢复所丢失的数据。
2.2 行动操作会对RDD 计算出一个结果,并把结果返回到驱动器程序中,或把结
果存储到外部存储系统(如HDFS)中.
pythonLines.first()
first() 就是我们之前调用的一个行动操作,它会返回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
在Scala 中使用行动操作对错误进行计数
println("Input had " + badLinesRDD.count() + " concerning lines")
println("Here are 10 examples:")
badLinesRDD.take(10).foreach(println)
我们在驱动器程序中使用take() 获取了RDD 中的少量元素。然后在本地
遍历这些元素,并在驱动器端打印出来。RDD 还有一个collect() 函数,可以用来获取整
个RDD 中的数据。如果你的程序把RDD 筛选到一个很小的规模,并且你想在本地处理
这些数据时,就可以使用它。记住,只有当你的整个数据集能在单台机器的内存中放得下
时,才能使用collect(),因此,collect() 不能用在大规模数据集上。
3.惰性求值
转化操作和行动操作的区别在于Spark 计算RDD 的方式不同。
虽然你可以在任何时候定义新的RDD,但Spark 只会惰性计算这些RDD。它们只有第一次在一个行动操作中用到时,才会真正计算。这种策略刚开始看起来可能会显得有些奇怪,不过在大数据领域是很有道理的。比如,看看例3-2 和例3-3,我们以一个文本文件定义了数据,然后把其中包含Python 的行筛选出来。
如果Spark 在我们运行lines = sc.textFile(…) 时就把文件中所有的行都读取并存储起来,就会消耗很多存储空间,而我们马上就要筛选掉其中的很多数据。相反, 一旦Spark 了解了完整的转化操作链之后,它就可以只计算求结果时真正需要的数据。事实上,在行动操作first() 中,Spark 只需要扫描文件直到找到第一个匹配的行为止,而不需要读取整个文件。
Spark 使用惰性求值,这样就可以把一些操作合并到一起来减少计算数据的步骤。在类似
Hadoop MapReduce 的系统中,开发者常常花费大量时间考虑如何把操作组合到一起,以
减少MapReduce 的周期数。而在Spark 中,写出一个非常复杂的映射并不见得能比使用很
多简单的连续操作获得好很多的性能。因此,用户可以用更小的操作来组织他们的程序,
这样也使这些操作更容易管理。
4.向Spark传递函数
Spark 的大部分转化操作和一部分行动操作,都需要依赖用户传递的函数来计算。
5.常见的转化操作和行动操作
3.持久化
把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 会对计算进行
优化后再执行。

本文介绍了Spark中的Resilient Distributed Datasets (RDD)的基本概念,包括创建方式(读取数据集和并行化)、两大操作类型(转化和行动),以及惰性求值策略。转化操作如filter()和map()产生新的RDD而不改变原数据,而行动操作如count()和collect()则返回结果。Spark使用惰性求值以减少不必要的计算,同时支持用户传递函数以实现各种操作。持久化操作允许中间结果在计算过程中被缓存,提高效率。
1896

被折叠的 条评论
为什么被折叠?



