广播变量

广播变量允许程序员保留一个只读的变量,缓存在每一台机器上,而非每个任务保存一份拷贝。他们可以这样被使用,例如,以一种高效的方式给每个结点一个大的输入数据集。Spark会尝试使用一种高效的广播算法来传播广播变量,从而减少通信的代价。

SparkContext.broadcast(v)方法从变量v创建的。广播变量是一个v的封装器,它的值可以通过调用value方法获得。如下模块展示了这个:

scala         >                   val          broadcastVar                   =                   sc         .         broadcast         (         Array         (         1         ,                   2         ,                   3         )         )


broadcastVar         :                   spark         .         Broadcast         [         Array         [         Int         ]         ]                   =                   spark         .         Broadcast         (         b5c40191         -         a864         -         4c7d         -         b9bf         -         d87e1a4e787c         )


                  


scala         >                   broadcastVar         .         value


res0         :                   Array         [         Int         ]                   =                   Array         (         1         ,                   2         ,                   3         )



在广播变量被创建后,它应该在集群运行的任何函数中,代替v值被调用,从而v值不需要被再次传递到这些结点上。另外,对象v不能在广播后修改,这样可以保证所有结点的收到的都是一模一样的广播值。


累加器

累加器是一种只能通过关联操作进行“加”操作的变量,因此可以高效被并行支持。它们可以用来实现计数器(如MapReduce中)和求和器。Spark原生就支持Int和Double类型的累加器,开发者可以自己添加新的支持类型。

一个累加器可以通过调用SparkContext.accumulator(v)方法从一个初始值v中创建。运行在集群上的任务,可以通过使用+=来给它加值。然而,他们不能读取这个值。只有驱动程序可以使用value的方法来读取累加器的值。

如下的解释器模块,展示了如何利用累加器,将一个数组里面的所有元素相加:



scala         >                  val         accum                  =                  sc         .         accumulator         (         0         )


accum         :                  spark         .         Accumulator         [         Int         ]                  =                  0


                  


scala         >                  sc         .         parallelize         (         Array         (         1         ,                  2         ,                  3         ,                  4         )         )         .         foreach         (         x                  =         >                  accum                  +         =                  x         )


.         .         .


10         /         09         /         29                  18         :         41         :         08                  INFO         SparkContext         :                  Tasks         finished         in                  0.317106                  s


                  


scala         >                  accum         .         value


res2         :                  Int                  =                  10


并行集合(Parallelized Collections)

SparkContext的parallelize方法,在一个已经存在的Scala集合上创建的(一个Seq对象)。集合的对象将会被拷贝,创建出一个可以被并行操作的分布式数据集。例如,下面的解释器输出,演示了如何从一个数组创建一个并行集合:

scala         >                   val          data                   =                   Array         (         1         ,                   2         ,                   3         ,                   4         ,                   5         )


data         :                   Array         [         Int         ]                   =                   Array         (         1         ,                   2         ,                   3         ,                   4         ,                   5         )


                  


scala         >                   val          distData                   =                   sc         .         parallelize         (         data         )


distData         :                   spark         .         RDD         [         Int         ]                   =                   spark         .         ParallelCollection         @         10d13e3e


一旦分布式数据集(distData)被创建好,它们将可以被并行操作。例如,我们可以调用distData.reduce(_ +_)来将数组的元素相加。我们会在后续的分布式数据集运算中进一步描述。

并行集合的一个重要参数是slices,表示数据集切分的份数。Spark将会在集群上为每一份数据起一个任务。典型地,你可以在集群的每个CPU上分布2-4个slices. 一般来说,Spark会尝试根据集群的状况,来自动设定slices的数目。然而,你也可以通过传递给parallelize的第二个参数来进行手动设置。(例如:sc.parallelize(data, 10)).



Hadoop数据集(Hadoop Datasets)

Spark可以从存储在HDFS,或者Hadoop支持的其它文件系统(包括本地文件,Amazon S3, Hypertable, Hbase等等)上的文件创建分布式数据集。Spark可以支持TextFile,SequenceFiles以及其它任何Hadoop输入格式。(Python接口目前还不支持SequenceFile,很快会支持吧)

Text file的RDDs可以通过SparkContext’s textFile的方式创建,该方法接受一个文件的URI地址(或者机器上的一个本地路径,或者一个hdfs://, sdn://,kfs://,其它URI). 下面是一个调用例子:

scala         >                  val         distFile                  =                  sc         .         textFile         (         "data.txt"         )


distFile         :                  spark         .         RDD         [         String         ]                  =                  spark         .         HadoopRDD         @         1d4cee08

distFile可以被进行数据集操作。例如,我们可以通过使用如下的map和reduce操作:distFile.map(_.size).reduce(_ + _ )将所有数据行的长度相加。

textFile方法也可以通过输入一个可选的第二参数,来控制文件的分片数目。默认情况下,Spark为每一块文件创建一个分片(HDFS默认的块大小为64MB),但是你也可以通过传入一个更大的值,来指定一个更高的片值。注意,你不能指定一个比块数更小的片值(和Map数不能小于Block数一样,但是可以比它多)

对于SequenceFiles,可以使用SparkContext的sequenceFile[K, V]方法创建,其中K和V是文件中的key和values的类型。像IntWritableText一样,它们必须是Hadoop的Writable interface的子类。另外,对于几种通用Writable类型,Spark允许你指定原生类型来替代。例如:sequencFile[Int, String]将会自动读取IntWritable和Texts。

SparkContext.hadoopRDD方法,它可以接收任意类型的JobConf和输入格式类,键类型和值类型。按照像Hadoop作业一样的方法,来设置输入源就可以了。

RDD 的操作

转换(transformation)从现有的数据集创建一个新的数据集;而动作(actions)在数据集上运行计算后,返回一个值给驱动程序。 例如,map就是一种转换,它将数据集每一个元素都传递给函数,并返回一个新的分布数据集表示结果。另一方面,reduce是一种动作,通过一些函数将所有的元素叠加起来,并将最终结果返回给Driver程序。(不过还有一个并行的reduceByKey,能返回一个分布式数据集)

map创建的一个新数据集,并在reduce中使用,最终只返回reduce的结果给driver,而不是整个大的新数据集。

persist(或者cache)方法,持久化一个RDD在内存中。在这种情况下,Spark将会在集群中,保存相关元素,下次你查询这个RDD时,它将能更快速访问。在磁盘上持久化数据集,或在集群间复制数据集也是支持的,这些选项将在本文档的下一节进行描述。

下面的表格列出了目前所支持的转换和动作(详情请参见 RDD API doc):

转换(transformation)

 转换

含义

map(func)

func函数转换后组成

filter(func)

func函数计算后返回值为true的输入元素组成

flatMap(func)

func应该返回一个序列,而不是单一元素)

mapPartitions(func)

func的函数类型必须是Iterator[T] => Iterator[U]

mapPartitionsWithSplit(func)

func带有一个整数参数表示分块的索引值。因此在类型为T的RDD上运行时,func的函数类型必须是(Int, Iterator[T]) => Iterator[U]

sample(withReplacement,fraction, seed)

fraction指定的比例,对数据进行采样,可以选择是否用随机数进行替换,seed用于指定随机数生成器种子

union(otherDataset)

返回一个新的数据集,新数据集是由源数据集和参数数据集联合而成

distinct([numTasks]))

返回一个包含源数据集中所有不重复元素的新数据集

groupByKey([numTasks])

在一个(K,V)对的数据集上调用,返回一个(K,Seq[V])对的数据集

注意:默认情况下,只有8个并行任务来做操作,但是你可以传入一个可选的numTasks参数来改变它

reduceByKey(func, [numTasks])

reduce函数,将相同key的值聚合到一起。类似groupByKey,reduce任务个数是可以通过第二个可选参数来配置的

sortByKey([ascending], [numTasks])

ascending布尔参数决定

join(otherDataset, [numTasks])

在类型为(K,V)和(K,W)类型的数据集上调用时,返回一个相同key对应的所有元素对在一起的(K, (V, W))数据集

cogroup(otherDataset, [numTasks])

groupwith

cartesian(otherDataset)

笛卡尔积,在类型为 T 和 U 类型的数据集上调用时,返回一个 (T, U)对数据集(两两的元素对)

完整的转换列表可以在RDD API doc中获得。

动作(actions)

 动作

含义

reduce(func)

func(接受两个参数,返回一个参数)聚集数据集中的所有元素。这个功能必须可交换且可关联的,从而可以正确的被并行执行。

collect()

在驱动程序中,以数组的形式,返回数据集的所有元素。这通常会在使用filter或者其它操作并返回一个足够小的数据子集后再使用会比较有用。

count()

返回数据集的元素的个数。

first()

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

take(n)

n个元素组成的数组。注意,这个操作目前并非并行执行,而是由驱动程序计算所有的元素

takeSample(withReplacement,num, seed)

num个元素组成,可以选择是否用随机数替换不足的部分,Seed用于指定的随机数生成器种子

saveAsTextFile(path)

toString方法,将它转换为文件中的文本行

saveAsSequenceFile(path)

将数据集的元素,以Hadoop sequencefile的格式,保存到指定的目录下,本地系统,HDFS或者任何其它hadoop支持的文件系统。这个只限于由key-value对组成,并实现了Hadoop的Writable接口,或者隐式的可以转换为Writable的RDD。(Spark包括了基本类型的转换,例如Int,Double,String,等等)

countByKey()

对(K,V)类型的RDD有效,返回一个(K,Int)对的Map,表示每一个key对应的元素个数

foreach(func)

func进行更新。这通常用于边缘效果,例如更新一个累加器,或者和外部存储系统进行交互,例如HBase

完整的转换列表可以在RDD API doc中获得。