• [Spark] Spark 对RDD编程


    本篇博客中的操作都在 ./bin/pyspark 中执行。

    RDD,即弹性分布式数据集(Resilient Distributed Dataset),是Spark对数据的核心抽象。RDD是分布式元素的集合,对手的所有操作都可以概括为:

    • 创建RDD
    • 转化已有RDD
    • 调用RDD操作进行求值

    在这些操作中,Spark会自动将RDD中的数据分发的集群上,并将操作自动化执行。

    每个RDD都被分为多个分区,这些分区运行在集群中的不同节点上。

    Get Started

    用户可以:

    • 读取一个外部数据集
    • 或者使用对象集合(比如 list 或者 set)

    来创建 RDD。比如使用 SparkContext.textFile() 来创建一个字符串RDD:

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

    RDD创建之后,支持:

    • 转化操作(transformation):会由一个RDD生成一个新的RDD。
    • 行动操作(action):会计算出一个结果,并把结果返回到驱动器程序中,或存储在外部存储系统中。

    Spark对RDD是惰性计算的,只有在行动操作(action)时,才会真正计算。

    回到shell 中,再执行:

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

    行动操作 first() 之中,Spark才进行真正的计算,而这时候只需要计算结果中真正需要的数据:在这里,Spark只需要扫面文件知道找到第一个匹配的行(包含"Python"的行)就停止了。

    默认情况下,Spark的RDD会在每次进行行动(Action)操作的时候重新计算,如果想在多个行动操作中使用同一个RDD,可以使用.persist()方法来让Spark把这个RDD缓存下来,这个操作叫做:持久化。

    持久化方便在以后的操作中重用数据。

    总的来说,Spark会这样工作:

    1. 创建出RDD
    2. 使用转化操作(比如filter)对RDD进行转化,创建出新RDD
    3. 告诉Spark我们要重用哪些中间结果,对这些RDD进行持久化操作
    4. 使用行动(Action)操作,来触发一次计算,Spark会对计算进行优化后再执行。

    另:cache()persist() 使用的默认存储级别是一样的。

    创建一个RDD

    使用外部数据集的方式比较常见,这里我们就看一个文本文档的例子:

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

    为了下面的演示不麻烦,我们这里主要看通过将程序中的集合转化为RDD的方法,快速创建一个RDD:

    lines = sc.parallelize(["Hello world", "News about Senate Hacking Hearing","US official says Russia undoubtedly meddled in US election"])
    

    // 注意上面的RDD中出现了两个"US",后面有用。

    对进行RDD操作

    RDD的转化操作是返回一个新的RDD的操作,比如.filter()操作就是转化操作。

    RDD的行动操作是向驱动器程序返回结果,或者把结果写入外部驱动器,行动操作会触发实际的计算,比如.count()或者 .first()方法。

    转化操作

    场景:找了个自己以前程序的log文件,我们使用Spark找出其中的错误(ERROR)信息,文件link

    下面是使用.filter()实现转化操作:

    >>> inputRDD   =  sc.textFile("url_Requests.log")
    >>> errorsRDD  =  inputRDD.filter(lambda x : "ERROR"   in x)
    >>> cautionRDD =  inputRDD.filter(lambda x : "CAUTION" in x)
    

    注意 .filter() 方法不会改变已有的 inputRDD 中的数据,该操作会返回一个全新的RDD,inputRDD还在后面的程序中还可以继续使用。

    然后再来一个.union() 操作:

    >>> badlineRDD = errorsRDD.union(cautionRDD)
    

    .union() 操作就是取并集,这个还比较好理解。

    通过转化操作,可以从已有的RDD中派生出新的RDD。

    行动(action)操作

    比如说.count()操作,就是一个行动操作:

    count

    另外一个常见的操作是.collect():

    collect-operation

    对于.collect()操作来说,可以用来获取整个RDD中的数据。只有当整个RDD的数据能在单台机器的内存中放得下时,才能使用该方法。

    当我们每次调用一个新的行动操作时,整个RDD都会从头开始计算,如果要避免这种行为,用户可以让中间结果持久化,这个在后面会提到。

    关于惰性求值

    RDD的转化都是惰性求值的,就是说在被调用行动曹组偶之前,Spark不会开始计算。

    惰性求值以为这我们对RDD调用转化操作是,操作不会立即执行,Spark会在内部记录下所有要执行的操作信息。我们可以把RDD当成我们通过转化操作构建出来的特定数据集。

    上面操作过的把文本数据读到RDD的操作同样也是惰性的,当我们调用 sc.textFile()时,数据并没有读取进来,而是在必要时才会读取。 和转化操作相同的是,读取数据的操作也有可能被多次执行。

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

    对各个元素的转化操作

    其中的一个例子是 .map()方法,map可以对RDD中的每个数据进行操作:

    >>> nums = sc.parallelize([1,2,3,4])
    >>> squared = nums.map(lambda x : x ** 2)
    >>> squared.collect()
    [1, 4, 9, 16]
    

    再比如我们刚才的日志文件:

    >>> numberOfLines = errorsRDD.map(lambda line: len(line))
    >>> numberOfLines.collect()
    

    这里,我们计算了每行错误日志的字符数,结果为:

    number-of-lines

    另一个是flatMap,看一个例子就懂了,还记得我们刚才创建的lines吗:

    lines.collect()
    words = lines.flatMap(lambda line: line.split(" "))
    words.collect()
    

    其输出结果为:

    words

    .map() 有什么区别呢,这是 map 的输出结果,很容易懂:

    difference-between-map-and-flatmap

    使用 .distinct() 操作进行去重:

    distinct

    常见行动操作

    .reduce() 是最常用的行动操作:

    reduce

    reduce 接受一个函数作为参数,这个函数要操作2个相同元素类型的RDD数据,并返回一个同样类型的新元素。

    此外,还有toptake 等常见操作:

    行动操作

  • 相关阅读:
    代码面试最常用的10大算法
    ant google compiler 压缩
    美工资源
    面试题
    validate表单验证插件
    laypage分页
    layer弹出框小结
    Thymeleaf
    webApp开发
    grunt自动化构建工具
  • 原文地址:https://www.cnblogs.com/guoyunzhe/p/6256314.html
Copyright © 2020-2023  润新知