【Spark】3.RDD编程

        本节主要针对Spark对数据的核心抽象——弹性分布式数据集(Resilient Distributed Dataset,简称RDD)。在Spark中对数据的所有操作不外乎创建RDD,转换已有RDD以及调用RDD操作进行求值。在这一切的背后Spark自动将RDD中的数据分发到集群上,并将操作并行化执行。

RDD基础

        Spark中的RDD是一个不可变的分布式对象集合,每个RDD都被分成多个分区,这些分区运行在集群的不同节点上。RDD中可以包含Python,Java,Scala中任意类型的对象,甚至用户自定义的对象。_
        创建RDD:两种方式——读取一个外部数据集或在驱动器程序里分发驱动器程序中的对象集合。
        创建出来的RDD支持两种类型的操作——转化操作和行动操作。转化操作会由一个RDD生成一个新的RDD,行动操作会对RDD计算出一个结果。
        注意虽然你可以在任何时候定义一个RDD,但是Spark只会惰性计算这些RDD,它们只会在对一次在一个行动操作中用到时,才会真正计算。
        其次Spark的RDD会在每次对它们进行行动操作时重新计算,如果需要在多个行动操作中重用一个RDD,可以使用RDD.persist()让Spark吧这个RDD缓存起来。
        一般来说每个Spark程序都按照如下方式进行工作:
                (1)从外部数据中创建出输入RDD
                (2)使用诸如fliter()这样的转化操作对RDD进行转化,以定义新的RDD
                (3)告诉Spark对需要被重用的中间结果RDD执行persist()操作
                (4)使用行动操作来触发一次并行计算,Spark会对计算进行优化后再执行

创建RDD

        Spark提供两种创建RDD的方式:读取一个外部数据集或在驱动器程序里分发驱动器程序中的对象集合。

        创建一个RDD最简单方式就是把程序中一个已有的集合传给SparkContext的parallelize()方法。

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

        更加常用的方式是从外部存储中读取数据来创建RDD,例如之前例子中的读取文本文件README的例子

lines = sc.textFlie("README.md")

RDD操作

        RDD支持两种类型的操作——转化操作和行动操作

1.转化操作
inputRDD = sc.textFlie("log.txt")
errorRDD = inputRDD.filter(lambda x : "error" in x)
warningRDD = inputRDD.filter(lambda x : "warning" in x)
badLineRDD = errorRDD.union(warningRDD)

        在上述例子中首先读取文本文件log.txt文件得到inputRDD输入RDD,在使用filter()方法分别获得日志文件中错误数据和警告数据RDD,在使用union()方法将两个RDD合并求并集。
        在Sprak中会使用谱系图来记录不同RDD之间的依赖关系。Spark使用这些信息按需计算每一个RDD,并在持久化数据丢失时进行数据恢复。

2.行动操作
print("Input had" + badLinesRDD.count() + "concerning lines")
print("Here are 10 example")
for line in badLinesRDD.take(10):
    print(line)

        上述例子中对badLinesRDD进行统计计数,并使用take()方法获取10个例子,最后遍历输出。

3.惰性求值

        RDD的转化操作是惰性求值的,着意味着在调用RDD进行行动操作时不会真正开始计算RDD。同时从外部读取数据转化RDD时也不是立刻执行的,数据并没有读取出来。惰性求值的方式可以将一些操作合并起来减少计算步骤,使操作更加容易管理。

向Spark传递函数

        在Python中拥有3种方式把函数传递给Spark——lambda表达式,顶层函数和定义的局部函数。

word = rdd.filter(lambda s : 'error' in s)

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

        需要注意的是,Python会在你不经意间把函数所在的对象也序列话传出,当你传递的对象是某个对象的成员时,或者包含了对某个对象中的一个字段的引用时,Spark会把整个对象发送到工作节点上。

class SearchFunctions(object):
    def __init__(self, query):
        self.query = query
    def isMatch(self, s):
        return self.query in s
    def getMatchesFunctionReference(self, rdd):
        return rdd.filter(self.isMatch) # 问题在self.isMatch中引用了整个self对象
    def getMatchesMemberReference(self, rdd):
        return rdd.filter(lambda x: self.qurey in x) # 问题在self.qurey中引用了整个self对象

        代替方案时,将需要的字段从对象中拿出来放到一个局部变量中,然后传递这个局部变量。

class SearchFunctions(object):
    ...
    def getMatchesMemberNoReference(self, rdd):
        query = self.query
        return rdd.filter(lambda x: qurey in x)

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

转化操作:

                                        表一:对数据为{1,2,3,3}的RDD基本转化操作

函数名目的示例结果
map()将函数应用与RDD的每一个元素,将返回值构成新的RDDrdd.map(x => x+1){2,3,4,4}
flatmap()将函数应用于RDD中的每一个元素,将返回的迭代器的每一个内容构成新的RDD。通常用来切片rdd.flatmap(x => x.to(3)){1,2,3,2,3,3,3}
filter()返回一个有通过穿给filter()的函数的元素组成的RDDrdd.filter(x => x!=1){2,3,3}
distinct()去重rdd.distinct(){1,2,3}
sample(withReplacement, fraction, [seed])对RDD采样,以及是否替换rdd.sample(false,0.5)非确定的

                                        表二:对数据分别为{1,2,3}和{3,4,5}的RDD进行针对两个RDD的转化操作

函数名目的示例结果
union()生成一个包含两个RDD中所有元素的RDDrdd.union(other){1,2,3,3,4,5}
intersection()求两个RDD共同元素的RDDrdd.intersection(other){3}
subtract()一处一个RDD中的内容rdd.subtract(other){1,2}
cartesion()与另一个RDD的笛卡尔积rdd.cartesion(other){(1,3),(1,4),(1,5),…(3,5)}

行动操作

                                        表三:对数据为{1,2,3,3}的RDD进行基本的RDD操作

函数名目的示例结果
collect()返回RDD所有的元素rdd.collect(){1,2,3,3}
count()返回RDD中元素的个数rdd.count()4
countByValue()各元素在RDD中出现的次数rdd.countByValue(){(1,1),(2,1),(3,2)}
take(num)从RDD中返回num个元素rdd.take(2){1,2}
top(num)从RDD中返回最前面的num个元素rdd.top(2){3,3}
takeOrdered(num)(ordering)从RDD中按照提供的顺序返回最前面的num个元素rdd.takeOrdered(2)(myOrdering){3,3}
takeSample(withReplacement,num,[seed])从RDD中返回任意一些元素rdd.takeSample(false,1)非确定性的
reduce(func)并行整合RDD中所有数据rdd,reduce((x,y) => x + y)9
fold(zero)(func)和reduce一样,不过需要提供初始值rdd.fold(0)((x,y) => x + y)9
aggregate(zeroValue)(seqOp,combOp)和reduce相似,但是通常返回不同类型的函数rdd.aggregate((0,0))(…)(9,4)
foreach(func)对RDD中的每一个元素使用给定的函数rdd.foreach(func)未知

持久化

Spark RDD是惰性求值的,有时对于一个RDD我们会重复使用,如果简单的调用,Spark每次都会重复计算RDD以及它的所有依赖。这时就需要对RDD进行持久化处理。
                                        表四:持久化级别

级别使用的空间cpu时间是否在内存中是否在磁盘上备注
MEMORY_ONLY
MEMORY_ONLY_SER
MEMORY_AND_DISK中等部分部分如果内存上放不下,溢写到磁盘
MEMORY_AND_DISK_SER部分部分如果内存上放不下,溢写到磁盘
DISK_ONLY
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值