RDD支持两种类型的操作:Transformation(从现有的数据集创建新的数据集)和action(在对数据集运行计算后将值返回给驱动程序)。例如,map是一种Transformation,它通过一个函数来传递每个数据集元素,并返回一个表示结果的新RDD。另一方面,reduce是一个action,它使用某个函数聚合RDD的所有元素,并将最终结果返回给driver驱动程序(尽管还有一个并行reduceByKey返回分布式数据集)。

spark中的所有的Transformation都是懒加载的,因为它们不会立即计算结果。相反,它们只记住应用于某些基本数据集(例如文件)的转换。只有当操作要求将结果返回到驱动程序时,才会计算转换。这种设计使Spark运行更有效。例如,我们可以意识到通过map创建的数据集将用于reduce,并且只将reduce的结果返回给驱动程序,而不是更大的映射数据集。

默认情况下,对每个已转换的RDD运行操作时,都可以重新计算它。但是,也可以使用persist(或cache)方法在内存中持久化RDD,在这种情况下,spark将保留集群中的元素,以便下次查询时更快地访问它。还支持在磁盘上持久化RDD,或跨多个节点复制RDD。

常见的Transformation

下表列出了Spark支持的一些常见Transformation。有关详细信息,请参考RDD API DOC(Scala、Java、Python、R)和配对RDD函数DOC(Scala,Java):

Transformation

Meaning

map(func)

返回一个新的分布式数据集,该数据集通过函数func传递源的每个元素而形成。

filter(func)

返回通过选择funcreturns为true的源元素形成的新数据集。

flatMap(func

与map类似,但每个输入项都可以映射到0个或多个输出项(因此func应该返回seq而不是单个项)。

mapPartitions(func

与map类似,但在RDD的每个分区(块)上单独运行,因此当在T类型的RDD上运行时,func必须是iterator<t>=>iterator<u>类型。

mapPartitionsWithIndex(func

与mapparties类似,但也为func提供了表示分区索引的整数值,因此在t类型的RDD上运行时func必须是类型(int,iterator<t>)=>iterator<u>

sample(withReplacement, fraction, seed

使用给定的随机数生成器种子对数据的一个分数部分进行采样,无论是否替换。

union(otherDataset)

返回一个新的数据集,该数据集包含源数据集和参数中元素的联合。

intersection(otherDataset)

返回一个新的RDD,其中包含源数据集中元素和参数的交集

distinct([numPartitions]))

返回包含源数据集的不同元素的新数据集。

groupByKey([numPartitions]) 

当调用(k,v)对的数据集时,返回(k,iterable<v>)对的数据集。注意:如果您分组是为了对每个键执行聚合(如求和或平均值),则使用ReduceByKey或AggregateByKey将产生更好的性能。注意:默认情况下,输出中的并行度级别取决于父RDD的分区数。您可以传递一个可选的numpartitions参数来设置不同数量的任务。

reduceByKey(func, [numPartitions]) 

当对(k,v)对的数据集调用时,返回一个(k,v)对的数据集,其中每个键的值使用给定的reduce函数func聚合,该函数的类型必须为(v,v)=>v。与groupbykey中一样,reduce任务的数量可以通过可选的第二个参数配置。

aggregateByKey(zeroValue)(seqOp, combOp, [numPartitions]) 

当调用(k,v)对的数据集时,返回(k,u)对的数据集,其中每个键的值使用给定的组合函数和中性“零”值进行聚合。允许与输入值类型不同的聚合值类型,同时避免不必要的分配。与groupbykey类似,reduce任务的数量可以通过可选的第二个参数进行配置。

sortByKey([ascending], [numPartitions]) 

当在k实现有序的(k,v)对的数据集上调用时,返回由键按升序或降序排序的(k,v)对的数据集,如Boolean Ascending参数中指定的那样。

join(otherDataset, [numPartitions])

 当对(k,v)和(k,w)类型的数据集调用时,返回一个(k(v,w))对的数据集,每个键外部联接的所有元素对都通过leftouter、rightouterjoin和fulloterjoin得到支持。

cogroup(otherDataset, [numPartitions]) 

当调用类型为(k vand(k w)的数据集时,返回(k(tenable<v>,iteable<w>)元组的数据集,其操作也称作aroupWith

cartesian(otherDataset)

当调用T和U类型的数据集时,返回一个(T,U)对(所有元素对)的数据集。

pipe(command, [envVars])

通过shell命令(例如perl或bash脚本)对RDD的每个分区进行管道连接。RDD元素写入进程的stdin,输出到其stdout的行作为字符串的RDD返回。

coalesce(numPartitions

将RDD中的分区数减少到numPartitions。对于过滤大型数据集后更高效地运行操作很有用。

repartition(numPartitions

随机重组RDD中的数据,以创建更多或更少的分区,并在分区之间进行平衡。这总是在网络上shuffles 所有数据。

repartitionAndSortWithinPartitions(partitioner

根据给定的分区对RDD重新分区,并在每个生成的分区内,按键对记录进行排序。这比调用重新分区和在每个分区内进行排序更有效,因为它可以将排序向下推送到随机播放机制中。

 常见的Actions

Action

Meaning

reduce(func)

使用函数func聚合数据集的元素(该函数包含两个参数并返回一个参数)。该函数应是可交换和关联的,以便能够正确并行计算。

collect()

在驱动程序中将数据集的所有元素作为数组返回。这通常在过滤器或返回足够小的数据子集的其他操作之后有用。

count()

返回数据集中的元素数。

first()

返回数据集的第一个元素(类似于take(1))。

take(n)

返回带有数据集前n个元素的数组。

takeSample(withReplacement, num, [seed])

返回一个数组,该数组包含数据集的随机samoleof num元素,可以替换,也可以不替换,OptionallyPre指定一个随机的numBeginGenerator种子

takeOrdered(n, [ordering])

使用自然顺序或Acustom比较器重新计算RDDU的前n个元素

saveAsTextFile(path)

在本地文件系统、hdfs或任何其他支持hadoop的文件系统的givendirectory中,将数据集的元素作为aext文件(或一组文本文件)写入。Spark将调用每个设备上的ToString来将其转换为文件中的一行文本

saveAsSequenceFile(path)

在本地文件系统、hdfs或任何其他支持hadoop的文件系统中,将数据集的元素作为ahadoop seguencerile写入给定路径中。在实现hadoop'swritable interface n scala的key-valuepairs的RDD上,它也可用于可写(spark includeconversions用于基本类型,如int.double string等

saveAsObjectFile(path)

使用JavaSerialIZATE以简单的格式对DataSet元素进行WRT,然后可以使用SparkContext.objectFile().

countByKey()

仅在类型为(k v.)的RDD上可用。返回(k int)pairs的哈希映射以及每个键的计数。

foreach(func)

对数据集的每个元素运行函数func。这通常是针对诸如更新累加器或与外部存储系统交互等副作用而进行的。注意:修改forEachMay之外的累加器以外的变量会导致未定义的行为。有关详细信息,请参见了解闭包

 

 

spark RDD API还公开了一些action的异步版本,比如foreach的foreachasync,它会立即向调用者返回FutureAction,而不是在操作完成时阻塞。这可用于管理或等待动作的异步执行。

Shuffle 操作

     Spark中的某些操作会触发一个称为shuffle的事件。shuffle是spark重新分配数据的机制,因此它在分区之间的分组方式不同。这通常涉及到跨执行器和机器复制数据。

背景描述

    为了理解shuffle过程中会发生什么,我们可以考虑reduceByKey操作的例子。reduceByKey操作生成一个新的RDD,其中一个key的所有values组合成一个tuple - the key,以及对与该key关联的所有values执行reduce函数的结果。挑战在于,并非所有单个key的值都必须位于同一分区,甚至同一台机器上,但它们必须位于同一位置才能计算结果。
    在Spark中,数据通常不分布在分区之间,而分布在特定操作所必需的位置。在计算过程中,单个任务将在单个分区上操作—因此,要组织所有数据以执行单个reduceByKey reduce任务,spark需要执行一个全对所有操作。它必须从所有分区中读取以查找所有键的所有值,然后将各个分区中的值组合在一起以计算每个键的最终结果——这称为shuffle

     虽然shuffle数据的每个分区中的元素集是确定的,分区本身的顺序也是确定的,但这些元素的顺序却是不确定的。如果你希望随机播放后按预期顺序排列数据,那么可以使用:

  • 对每个分区进行排序的映射分区,例如,.sorted
  • 重新分区和带分区的分区,以便在同时重新分区的同时高效地对分区排序
  • Sortby生成全球订购的RDD

    导致混乱的操作可能包括重新分区和合并等重新分区操作,GroupByKey和ReduceByKey等ByKey操作(计数除外),以及CoGroup和Join等联接操作。

影响性能的因素

shuffle是一项耗资源的操作,因为它涉及磁盘I/O、数据序列化和网络I/O。要为无序播放组织数据,Spark会生成一组任务-映射任务以组织数据,以及一组减少任务以聚合数据。这个术语来自map reduce,与spark的map和reduce操作没有直接关系。

在内部,来自单个映射任务的结果保存在内存中,直到它们无法容纳为止。然后,根据目标分区对它们进行排序,并将其写入单个文件。在reduce方面,tasks读取相关的排序块。

某些shuffle操作可能会消耗大量堆内存,因为它们在传输记录之前或之后使用内存中的数据结构来组织记录。具体来说,reduce bykey和aggregatebykey在映射端创建这些结构,而“bykey操作在reduce端生成这些结构。当数据不适合内存时,spark会将这些表溢出到磁盘上,从而导致磁盘I/O的额外开销和增加的垃圾收集。

shuffle还会在磁盘上生成大量中间文件。从spark 1.3开始,这些文件将被保留,直到不再使用相应的RDD并被垃圾收集。这样,如果重新计算沿袭,则无需重新创建无序播放文件。如果应用程序保留对这些RDD的引用,或者GC不经常启动,垃圾收集可能只在很长一段时间之后发生。这意味着长时间运行的Spark作业可能会消耗大量的磁盘空间。配置Spark上下文时,临时存储目录由spark.local.dirconfiguration参数指定。

可以通过调整各种配置参数来调整无序播放行为。可参阅spark配置指南中的“Spark Configuration Guide.”部分。