大数据Spark企业级实战版【学习笔记】----RDD:分布式函数式编程

来源:互联网 发布:知乎名字由来 编辑:程序博客网 时间:2024/05/29 10:11

1.2.2 RDD:分布式函数式编程

1. RDD的基本概念

       在Spark中,一个RDD就是一个分布式对象集合。每个RDD可分为多个分片(partitions)。而分片可以在集群环境的不同节点上计算。RDD兼容Python、Java或者Scala的对象,包括用户定义的类。

       用户可以通过两种方式创建RDD:加载外部数据集、在驱动程序中部署对象集合。

       可以通过SparkContext.textFile()来加载一个将文本文件作为字符串的RDD:

        

       RDD被创建后,提供两种类型的操作:转换(transformation)、动作(action)。转换是将原来的RDD构建成新的RDD。比如:

       

       而动作是通过RDD来计算的结果,并且将结果返回给驱动程序或者保存到外部存储系统(如HDFS)。例子如下:

        

       first()返回RDD的第一个元素。

       动作和转换的不同之处取决于Spark计算RDD的方式。可以在任何时间创建新的RDD,Spark采用一种慵懒的态度计算它们---第一次使用它们中的动作时才计算。例如,考虑上面的例子,在这里定义一个文本文件,然后过滤含有“right”的行。如果Spark加载和存储所有中间过程,就会浪费大量的存储空间,因为随后会立即过滤掉一部分不需要的行。相反,一旦Spark看到整个变换链,它可以计算仅需要其结果的数据。对于first()动作,Spark只扫描文件,直到它找到第一个匹配的行,它甚至不读整个文件!

       Spark的RDDS在默认情况下每次运行它们都要进行重新计算。如果需要重用在多个动作中,可以使用Spark的持久化方法:RDD.persist()计算它的第一次后,Spark将RDD内容存储在内存中(整个机器的集群分区),并在未来的动作总重新使用它们。持久化在磁盘上,而不是存储在内存中也是可行的。默认情况下动作不进行持久化,因为会浪费过多的存储空间。

       实际上,一般会经常使用持久化去加载数据集到内存中,以便可以重复地查询和使用。比如,如果想计算多个关于"right"的结果,会想下面这样写:

        

2. 创建RDD

       Spark提供两种创建RDD的方式:加载外部数据集、在驱动程序中平行化集合

       创建RDD最简单的方法就是采用现有的内存集合并把它传递给SparkContext的并行化方法(spark.parallelize())。

        

       创建RDD一个更常见的方法是加载外部存储数据。可以使用SparkCOntext.textFile()来创建一个RDD:

       

3. RDD操作

       RDD支持两种类型的操作:转换和动作。转换是上一个RDD返回一个新的RDD,如映射与过滤器。动作是返回结果并将结果返回给驱动程序或写入到存储,并行计算,如count()和first()。Spark对待转换和动作非常不同,所以理解正在执行的是哪一类操作类型是非常重要的。如果不知道一个给定的function是转换还是动作,可以看它返回类型:转换返回RDD、动作返回一些其他数据类型。

1)       转换

      转换是RDD操作的一种类型,每次进行转换操作都会返回一个新的RDD对象。转换RDD的计算方式是惰性的,只有在动作中使用它们才会进行计算。

   

       filter()的操作不改变现有的inputRDD。相反,它返回一个全新的RDD。inputRDD仍可以在以后的程序重新使用,例如,搜索“error”意外的其他词语,使用inputRDD再次搜索含有“warning”的行,然后使用另一种转换union(),打印出含有“error”或“warning”的行。

  

       union()和filter()有点不同,因为它操作在两个RDD之上而不是一个RDD。转换可以在任意数量的RDD上进行操作。当相互转换得到并使用新的RDD时,Spark跟踪记录切设定不同的RDD之间的依赖关系,这种关系称为血统图。它使用这个信息来按照需求计算每个RDD,以及恢复持续化的RDD丢失那一部分数据。

      

       图1-33给出血统图例子,包括inputRDD和结束的两个动作。每次调用一个新的动作,整个RDD必须“从头开始”计算为了调高效率,用户可以将这些中间结果持久化。

2)       动作

       动作是第二类RDD操作,它们返回一个最终值到驱动程序或返回数据写入到一个外部存储系统动作迫使所需的转换被调用执行,因为它们需要实际产生输出。

       以上述例子继续,如果想打印出一些关于badLinesRDD的信息。为了做到这一点,使用两个动作:count()返回计数,为一个数字值;take()包含大量来自RDD的元素。

      

       使用take()在驱动程序中检索一个小数目的RDD元素。然后,在驱动程序中遍历它们并在本地打印出它们的信息。RDDS还有一个collect()函数,用来获取整个RDD整个数据集必须在一台机器上装入内存中再使用collect(),因此collect()不应该用在大型数据集上

       在大多数情况下,RDD不能仅仅被collect()到驱动,因为太大。在这些情况下,常见的做法是把数据写到一个分布式的存储系统中,如HDFS或者Amazon S3。RDD的内容可以用savaAsTextFile()或者savaAsSequenceFile()以及其他动作来保存。

3)       惰性评估(Lazy Evaluation)

      惰性评估意味着,当调用RDD的转换时,不立即执行该操作。想法,Spark在内部记录元数据以表明该操作已被请求,而不是考虑RDD包含的具体的数据。最好是把每个RDD的转换操作看作关于计算数据的指令。将数据加载到RDD中也是以同样的方式(惰性的方式)转换的。因此,当执行SparkContext.textFile()时,数据并没有被加载,直到它有动作需要执行是才加载数据

       转换是惰性的,迫使Spark在任何时间只能通过一个动作来执行它们,如count()。Spark通过使用惰性评估,以减少其在各种转换操作中所需要存储的中间数据

阅读全文
0 0
原创粉丝点击