spark读书笔记(1)-RDD

1. RDD基础

RDD支持两种操作,transformation和action。本质区别,转化操作生成新的RDD,行动操作计算结果返回驱动器程序中,或存储到外部存储器。
理解:两种操作的计算方式不同,由于spark是惰性计算,可以任何时刻定义新的RDD但是只有行动操作时才会出发实际计算。

pythonLines = lines.filter(lambda line: "Python" in line)
pythonLines.first()

例如,如果运行第一行代码就把文件中所有的行都读取浪费资源,而惰性计算则是当遇到action后,first()只是检索第一行,这样节省大量资源。
总的来说,每个Spark 程序或shell 会话都按如下方式工作。
(1) 从外部数据创建出输入RDD。
(2) 使用诸如filter() 这样的转化操作对RDD 进行转化,以定义新的RDD。
(3) 告诉Spark 对需要被重用的中间结果RDD 执行persist() 操作。
(4) 使用行动操作(例如count() 和first() 等)来触发一次并行计算,Spark 会对计算进行
优化后再执行。

2 创建RDD

创建RDD两种方式:读取数据;在driver program中对集合进行并行化。
python的读取文本方法:

lines = sc.textFile("/path/to/README.md")

python的并行化方法:

lines = sc.parallelize(["pandas", "i like pandas"])

3. RDD操作

3.1 转化操作

例3-1 找到日志中error行

inputRDD = sc.textFile("log.txt")
errorsRDD = inputRDD.filter(lambda x: "error" in x)

转化操作不会改变已有inputRDD,派生新的RDD。

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

spark使用谱系图(lineage graph)记录RDD的依赖关系。Spark 需要用这些信息来按需计算每个RDD,也可以依靠谱系图在持久化的RDD 丢失部分数据时恢复所丢失的数据。

3.2 行动操作

print("Input had " + badLinesRDD.count() + " concerning lines")
print("Here are 10 examples:")
for line in badLinesRDD.take(10):
    print(line)

例子中,在驱动器程序中利用take()获取前10条数据,然后在本地遍历,再在驱动器打印。
每当我们调用一个新的行动操作时,整个RDD 都会从头开始计算。要避免这种低效的行为,用户可以将中间结果持久化。
理解惰性求值:不应把RDD当作存放数据的数据集,而是记录如何计算(转化)数据的指令列表。惰性求值的主要目的是把一些操作合并,以减少计算数据的步骤。而在Spark 中,写出一个非常复杂的映射并不见得能比使用很多简单的连续操作获得好很多的性能。因此,用户可以用更小的操作来组织他们的程序,这样也使这些操作更容易管理。

4. 传递函数

python中三种方式实现函数传递:lambda;顶层函数;局部函数。

word = rdd.filter(lambda s: "error" in s)
def containsError(s):
    return "error" in s
word = rdd.filter(containsError)

传递函数时需要小心的一点是,Python 会在你不经意间把函数所在的对象也序列化传出去。当你传递的对象是某个对象的成员,或者包含了对某个对象中一个字段的引用时(例如self.field),Spark 就会把整个对象发到工作节点上。
替代的方案是,只把你所需要的字段从对象中拿出来放到一个局部变量中,然后传递这个局部变量。

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

针对各个元素的转化操作
map()接收一个函数,把函数用于RDD的每个元素,返回新RDD。返回值类型不需要与原RDD相同。
filter()接收一个函数,把原RDD中符合函数的元素放入新RDD返回。

nums = sc.parallelize([1, 2, 3, 4])
squared = nums.map(lambda x: x * x).collect()
for num in squared:
    print ("%i " % (num))

flatMap(),返回一个返回值序列的迭代器,输出的RDD倒不是由迭代器组成的。我们得到的是一个包含各个迭代器可访问的所有元素的RDD。
例,切分单词

lines = sc.parallelize(["hello world", "hi"])
words = lines.flatMap(lambda line: line.split(" "))
words.first() # 返回"hello"

这里写图片描述
伪集合操作
简单集合操作:
这里写图片描述
RDD.distinct() 转化操作来生成一个只包含不同元素的新RDD。不过,distinct() 操作的开销很大,因为它需要将所有数据通过网络进行混洗(shuffle),以确保每个元素都只有一份。
union(other),它会返回一个包含两个RDD 中所有元素的RDD。这在很多用例下都很有用,比如处理来自多个数据源的日志文件。与数学中的union() 操作不同的是,如果输入的RDD 中有重复数据,Spark 的union() 操作也会包含这些重复数据(如有必要,我们可以通过distinct() 实现相同的效果)。
intersection(other) 方法,只返回两个RDD 中都有的元素。intersection()在运行时也会去掉所有重复的元素(单个RDD 内的重复元素也会一起移除)。
对一个数据为{1, 2, 3, 3}的RDD进行基本的RDD转化操作:
这里写图片描述
对数据分别为{1, 2, 3}和{3, 4, 5}的RDD进行针对两个RDD的转化操作:
这里写图片描述
行动操作
reduce()它接收一个函数作为参数,这个函数要操作两个RDD 的元素类型的数据并返回一个同样类型的新元素。(累加实现的是求和)。

sum = rdd.reduce(lambda x, y: x + y)

collect(),将整个RDD 的内容返回,要求所有数据都必须能一同放入单台机器的内存中。
take(n),返回RDD 中的n 个元素,并且尝试只访问尽量少的分区,因此顺序可能与预期不同。
top(),如果为数据定义了顺序,从RDD 中获取前几个元素。
takeSample(withReplacement, num,seed),数据采样。
foreach(),对RDD中的每个元素进行操作,而不需要把RDD 发回本地。

对一个数据为{1, 2, 3, 3}的RDD进行基本的RDD行动操作:
这里写图片描述

持久化

RDD.persist()


猜你喜欢

转载自blog.csdn.net/sky_noodle/article/details/81483461