Spark1.1.1官网文档翻译4Spark编程指南

Spark 编程指南

在较高水平上,每个Spark应用由一个程序驱动在集群上面运行用户的主要功能和Main方法。主要的Spark抽象是一个弹性分布式数据集(RDD)这是一个在集群节点上可以进行划分并能够并行执行的元素。RDD是从Hadoop文件系统创建的文件(或者其他Hadoop支持的文件系统)或者是一个现有的Scala集合中的驱动程序转化而来。用户也可以要求RDD持久化,可以有效的并行重复执行使用。最后,RDD会自动从节点故障中恢复。

共享变量是Spark第二个并行操作的抽象,默认情况下,Spark每一组平行执行同一个方法任务的节点上面,为每一个方法执行的任务节点逐个进行变量拷贝。有时候,一个变量需要被很多任务访问到,或者需要驱动程序和节点间进行选择。Spark支持两种类型的共享变量:广播的变量,可以用来缓存内存中所有节点上的值,还有一个是累加器,这是唯一的“添加”变量,用于计数器和计算总和。

本文介绍了Spark支持的每一种语言的使用,你可以很容易的与Spark进行交互Scala shell或者bin/pyspark

 

Spark链接

Spark1.1.1使用Scala2.10,编写scala程序,你需要使用正确的Scala版本号。写一个Spark程序时,你需要添加一个Maven配置文件,在里面进行配置

groupId=org.apache.spark

artifactId=spark-core_2.10

version=1.1.1

此外,如果你需要连接到一个HDFS集群,你需要添加一个hadoop-client配置你的HDFS版本,一些HDFS版本配置信息请参考

http://spark.apache.org/docs/latest/hadoop-third-party-distributions.html

groupId=org.apache.hadoop

artfactId=hadoop-client

version=<your-hdfs-version>

 

--------------------------附:关于hadoop-version的属性-----------------------

mvn -Dhadoop.version=1.0.4 -DskipTestsclean package

mvn -Phadoop2.2 -Dhadoop.version=2.2.0-DskipTests clean package

 

以下表格对应相应的CDH-HDP版本号

CDH 4.X.X(Yarn mode)

2.0.0-cdh4.X.X

CDH 4.X.X

2.0.0-mr1-cdh4.X.X

CDH 3u6

0.20.2-cdh3u6

CDH 3u5

0.20.2-cdh3u5

CDH 3u4

0.20.2-cdh3u4

HDP 1.3

1.2.0

HDP 1.2

1.1.2

HDP 1.1

1.0.3

HDP 1.0

1.0.3

HDP 2.0

2.2.0

 

SBT中,设置hadoop.version属性

sbt/sbt -Dhadoop.version=1.0.4assembly

 

在应用程序连接hadoop版本

除了添加Spark正确的版本,你还需要加上Maven配置文件中的hadoop-client到每一个Spark程序中运行,所以我们可以添加HDFS版本号到集群上面,如果你用的是CDH,你需要添加ClouderaMaven库,这是SBT的示例

libraryDependencies +="org.apache.hadoop"%"hadoop-client"% "<version>"

//If useing CHD,also add Cludera repo

resolvers +="ClouderaRepository" at

"https://repository.cloudera.com/artifactory/cloudera-repos/"

 

以下是maven的示例

<project>

    <dependencies>

    ...

        <dependency>

            <groupId>org.apache.hadoop</groupId>

            <artifactId>hadoop-client</artifactId>

            <version>[version]</version>

        </dependency>

    </dependencies>

<!--If using CDH,also add Clouderarepo -->

<repositories>

    ...

    <repository>

        <id>Clouderarepository</id>

        <url>https://repository.cloudera.com/artifactory/cloudera-repos/</url>

    </repository>

</repositories>

</poject>

从集群配置继承

如果你计划在Spark中读写HDFS,这里有两个Hadoop配置文件需要加入Spark的ClassPath

hdfs-site.xml 提供HDFS客户端默认行为

core-site.xml 提供默认文件名

这些配置文件位置因为版本的不同而不同比如CDH或者HDP,一个常见的位置是/etc/hadoop/conf里面有一些工具,比如ClouderaManager可以动态配置,不过可以通过一个机制来下载的副本

为Spark配置这些文件,设置HADOOP_CONF_DIR到$SPARK_HOME/spark-env.sh到本地文件设置

http://spark.apache.org/docs/latest/hardware-provisioning.html#storage-systems

http://spark.apache.org/docs/latest/spark-standalone.html

------------------------附:关于hadoop-version的属性结束---------------------

 

最后,你需要引入Sparkclasses和隐式转换到你的程序中

import org.apache.spark.SparkContext

import org.apache.spark.SparkContext._

import org.apache.spark.SparkConf

 

初始化Spark

编写Spark程序第一件事情就是创建一个SparkContext对象,用来告诉Spark怎样连接到集群。创建SparkContext你需要先创建一个SparkConf对象

val conf=newSparkConf().setAppName(appName).setMaster(master)

new SparkContext(conf)

appName变量是集群UI上面的一个名称,master可以是Spark、Mesos或者Yarn Cluster又或者是local用来表示在本地模式运行。在实践中,集群上运行的时候,你不想硬编码到程序中去,而是使用Spark-submit启动应用程序并接收它。然而,在本地和单元测试中,你可以用local运行Spark。

 

使用shell

在Spark Shell中,一些特殊的解释器叫做sc的Spark-Context已经为你创建好了。在你自己的程序上面它不会工作。你可以用--master设置context连接哪一个master,另外你可以使用--jar添加Jar包到classpath并通过一个逗号进行分隔,bin/spark-shell将Spark运行在一个四核中:

./bin/spark-shell --master local[4]

添加code.jar到classpath,使用以下语句

./bin/spark-shell --master local[4]--jars code.jar

使用--help获得一个完整的Spark参数列表,在spark-shell和spark-submit中

 

弹性分布式数据集(RDD)

Spark围绕着一个弹性分布式数据集的概念(RDD)这是一个容错并且可以进行并行操作的元素。有两种方法创建RDDs:在驱动程序中并行现有集合产生,或者从一个外部的存储系统,如共享文件系统,HDFS,Hbase,或者任何数据源能够提供一个HadoopInputFormat

 

并行化的集合

并行化集合是通过SparkContext的parallelize方法把一个现有的集合再你的驱动程序中创建(a scala seq)集合的元素复制并形成一个分布式数据集(RDD),可以并行操作,例如,这里创建一个并行数据集数字1到5

 

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

val distData=sc.parallelize(data)

 

一旦创建,分布式数据集(distdata)可以并行操作,例如,在我们创建了分布式数据集以后,可以使用distData.reduce((a,b)=>a+b)添加到数组元素。

并行化的一个重要参数是把集群分片时候的数目。Spark会为集群中每个分片创建一个任务。通常你需要在每个集群cpu中使用2-4个分片。正常情况下,Spark试图基于集群自动进行分片。然而,你也可以手动设置第二个参数来控制它的并行化(例如sc.parallelize(data,10))

 

外部数据集

Spark可以创建任何存储资源来源的分布式数据集,包括你的本地文件系统,HDFS、Cassandra、Hbase、Amazon S3等,Spark支持文本文件,sequencefiles,和任何其他的HadoopInputFormat

TextFileRDDs可以使用SparkContext的textFile方法创建,这个方法需要一个URI地址文件(无论本地路径,hdfs:// s3n:// 等等)并读取它作为一个数据集合,例如以下方式

 

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

distFile:RDD[String]=MappedRDD@1d4cee08

 

一旦创建,可以对对distFile进行数据操作,例如我们可以利用map和reduce添加一些数值到左右行上面:distFile.map(s=>s.length).reduce((a,b)=>a+b)

 

Spark读文件的一些要点

如果路径使用本地文件系统(local),那么文件还必须和工作节点相同的路径。可以将文件复制到所有工作节点使用的网络文件系统中去。

 

Spark的所有输入形式,方式和方法,支持目录以及通配符。例如可以使用textFile("/my/directory"),textFile("/my/directory/*.txt"),textFile("/my/directory/*.gz")

 

textFile方法,还有一个可选的参数控制分片数量,默认情况下,Spark把产生的每一个块的文件作为一个分片(默认情况下HDFS 64MB)但你也可以要求更高的分片数量,通过传入一个较大的值,注意!你不能设置更少的块数量

 

除了文本文件Spark的ScalaAPI还支持其他数据格式

SparkContext.wholeTextFiles能够使你读取一个包含多个小文件的目录,并返回每个文件的(名称,内容)键值对,这是进行文本文件文件对比,并将每行作为一个记录的文件进行划分。

 

对于sequenceFiles使用SparkContext.sequenceFile[K,V]方法,其中K和V作为键和值的类型,这些应该是HadoopWriteable接口的子类比如IntWriteable和Text。另外,Spark还允许你对一些常见的Writables指定本地类型,例如sequenceFile[Int,String]会自动读取IntWritable和Text

其它的Hadoop InoutFormats你可以使用SparkContext.hadoopRDD方法,以任意JobConf和inputFormatClass的class,key class,valueclass。这些值通过一个相同的方式和你的hadoop输入源一同工作。你也可以对新MapReduceAPI(org.apache.hadoop.mapreduce)使用SparkContext.newHadoopRDD方法

 

RDD.saveAsObjectFile和SparkContext.objectFile由java对象序列化方式进行保存。虽然这不是使用Avor这种专门的格式那样有效,但它提供了一个简单的方法来保存任意的RDD

 

RDD操作

RDDs支持两种类型的操作:transformations(转换),从现有的一个转换成另一个,和Action它返回一个计算后的数值。例如 map是一个transformations,数据集的每一个元素通过一个函数,返回后成为另一个数据集。另一方面,reduce是一个Action使用一些功能用RDD返回结果的一种驱动行为(虽然也有一个平行的reduceByKey返回一个RDD)

Spark的所有transmations都是延迟加载的,它们并不计算结果,而仅仅是变换一些基础应用数据(如文件)只有当一个Action需要结果作为输入时,transmations才会被执行。这种设计使得Spark能够更有效率的运行,举个例子,我们可以通过map来duidataset进行解析然后使用reduce返回最终的结果,避免map中传递更大的数据集。

默认情况下,每个tiansformation RDD可以在运行之后重新计算。然而,你也可以将结果保存在内存中进行持久化RDD(或缓存)的方法。在这种情况下,Spark将保持元件在下次查询时进行最快速的访问,也可以持久化到磁盘上,在多个节点间进行复制。

 

基础

val lines =sc.textFile("data.txt")

val lineLengths=lines.map(s=>s.length)

val totalLength=lineLiengths.reduce((a,b)=>a+b)

第一句使用了一个外部数据建立了一个RDD,该数据集并不是从内存中加载或者已经启动执行:仅仅是一个指向文件的说明,再一次,由于延迟加载执行,lineLengths也不是立即计算。最后我们运行reduce,这是一个Action,在这一点上Spark开始在分别的机子上执行计算,每台机子上运行的map和一个本地的reduce,最终仅仅返回需要的答案给驱动程序。

如果想要在稍后时候使用lineLengths,我们可以这样

 

lineLengths.persist()

 

第一次计算后,在reduce执行前,lineLengths被保存在内存中

 

传递一个函数给Spark

Spark的API在很大程度上依赖于驱动程序在集群间的函数传递,有两个方法来做这个:

匿名的函数语法,它可以用于短的代码块

在一个全局的静态方法,例如,你可以定义对象myfunctions然后通过myfunctions.func1来调用

 

/---------------------------匿名函数语法-----------------------------/

Scala定义匿名函数提供了一个相对轻量级的语法,下面的表达式创建了一个整数加1的函数

 

(x:Int)=> x+1

 

这是以下匿名类的简单版本

 

new Function(Int,Int){

                 def apply(x:Int):Int = x+1

}

 

它可以定义多个参数

 

(x:Int,y:Int)=>"("+x+","+"y"+")"

 

或者没参数的

 

()=>[System.getProperty("user.dir")]

 

还有一个写函数的非常轻量级的方法,这里是三个功能上面定义的类型

 

Int=>Int

(Int,Int)=>String

()=>String

 

这些函数是以下函数的简写

 

Function1[Int,Int]

Function2[Int,Int,String]

Function0[String]

 

/---------------------------匿名函数语法结束-----------------------------/

 

举例:

Object MyFucntions{

                 deffunctions(s:String):string={...}

}

myRdd.map(MyFunctions.func1)

 

注意,也可以通过引用一个类示例的方法(而不是一个单独的对象),这就要求发送包含跟随方法的类的对象,例如:

classMyClass{

                 def fcunc(s:String):String ={...}

                 defdoStuff(rdd:RDD[String]={rdd.map(func1)})

}

 

这里,如果我们创建一个MyClass调用doStuff方法,map中的引用func1是MyClass一个内部对象实例。所以整个对象需要被发送到集群上面,类似于在写rdd.map(x=>this.func1)

 

类似的方式,外部域访问对象将整个对象作为参考

 

classMyClass{

                 val field="Hello"

                 defdoStuff(rdd:RDD[String]):RDD[String]={rdd,map(x=>field + x)}

}

 

等于写一个rdd.map(x=> this.field + x)它引用的这所有的一切。为了避免这个问题最简单的方法是复制字段到一个本地属性而不被外部访问到。

 

defdoStuff(rdd:RDD[String]):RDD[String]={

                 val field_=this.field

                 rdd.map(x=>field_+x)

}

 

以key-value方式工作

虽然Spark大部分工作是使用RDD控制任何类型的对象,一些特殊的操作仅仅在RDD的键值上面有效,最常见的方式是分布式的“shuffle”操作,比如分组或者聚集时候的的关键key

在Scala中,这些操作都是自动可用的,比如RDDs包含的Tuple2对象(内置对象的元组中的语言,由简单的writing(a,b)创建)你要你import org.apache.spark.SparkContext._到你的程序中进行Spark的隐式转换,key-value操作对于PairRDDFunctions类就是可用的,它自动围绕RDD元组进行转换。

例如,下面的代码将reduceByKey操作键值对的每行文件的次数出现到一个文件中

 

val lines=sc.textFile("data.txt")

val pairs=lines.map(s=>(s,1))

val counts=pairs.reduceByKey((a,b)=>a+b)

 

我们还可以使用counts.sortByKey()例如,按字母顺序排序,并counts.collect()返回给对象一个数组

 

注意:当使用自定义对象作为key在key-value进行作业,你必须确保自定义equals()方法伴随着一个匹配的hashcode()方法。具体内容,请参考java文档。

 

Transformations

下面列举了Spark一些常用的Transformation

map(func)

通过一个函数形成一个新的分布式数据源

filter(func)

通过函数判断是否为true,由来源返回一个新的数据集

flatMap(func)

类似map,但每个输入项可以被映射到0个或多个的输出项(函数应该返回一个序列而不是一个单一的项目)

mapPartitions

类似map,但运行分别在每个RDD分区(block)func必须是迭代器类型的Iterator<T>=>Iterator<U>运行时,RDD的类型是T

mapPartitionsWithIndex

类似mapPartitons但是func提供一个整数值代表partition的序列(Index)所以func必须是迭代器类型的Iterator<T>=>Iterator<U>运行时,RDD的类型是T

sample(withReplacementfraction,seed)

Data的一个采样数字更换或者不更换,使用一个随机生成器产生(seed)

uniom(otherDataset)

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

intersection(otherDataSet)

返回一个存在交集的数据的RDD

distinct([numTasks])

返回一个包含不同元素数据集

groupByKey([numTasks])

使用数据集上的(k,v)对,返回一个数据集(k,Iterator<v>)

注意:如果你是在每个key上面分组进行聚合(如总和或者平均值),使用reduceByKey或者combineByKey会产生更好的性能

默认情况下,在输出的并行水平取决于RDD的分区数,你可以通过一个可选的numtasks参数设置不同的任务数

reduceByKey(func,[numTasks])

使用数据集上的(k,v)对,返回一个数据集(k,v)对每个关键值聚合使用给定的减少任务的函数式,他必须是类型(V,v)=> V就像是GroupBy,可以使用参数设置一个任务数量

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

使用数据集上的(k,v)对,返回一个数据集(k,u)对每个关键值聚合使用指定的聚合函数和一个中性的0值。允许不同的输入类型返回一个集合的值类型。同时避免了不必要的配置。类似GroupBy,可以使用参数设置一个任务数量

sortByKey([ascending],[numTasks])

使用数据集的(k,v)对K执行指令,返回数据集(k,v)按照键的升序或者降序排列由布尔参数指定,默认升序

join(otherDataset[numTasks])

当对元素类型(k,v)和(k,w)返回一个数据集(k,(v,w))的所有元素对应的键值对,外连接也可以通过leftOuterJoin和rightOuterJoin指定

cogroup(otherDataset,[numTasks])

对数据集类型(k,v)和(k,w)返回一个数据集(k,Iterator<v>,Iterator<w>)元组,这个操作也被称为groupWith

cartesian(otherDataSet)

当在类型t和u的数据集,返回数据集的(t,u)对(包含所有元素的对)

pipe(command,[envVars])

对RDD管道通过一个shell命令,比如Perl或者bash脚本。RDD元素写入进程的stdin和stdout管道进行输出类似一个RDD产生的字符串

coalesce(numPartitions)

减少numPartitions在RDD分区中的数量。用于更有效率的过滤下大量数据后

repartition(numPartitoons)

Shuffle数据可以在RDD上面随机创造出更多的或者更少的平衡数量的分区,可以将数据平均分配在网络上。

 

Actions

下面列出一些常见的Action

reduce(func)

集合元素使用的功能函数(需要两个参数并返回一个)这个功能必须使得并行交换执行结果计算正确

collect()

驱动程序的数组中,返回所有元素的数据集。这通常是用过滤器或者其他操作反悔了足够小的子集以后

count()

返回数据集的元素数量

first()

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

take(n)

返回一个数组的前N个数据元素,请注意,这个目前无法并行执行。必须使用驱动程序的所有元素

takeSample(withRepalcement,num,[seed])

返回一个随机样本的数据集数字元阵列,无需更换,可以预先制定生成器的种子

takeOrdered(n,[ordering])

第一个元素n是指他们使用RDD或者自定义的comparator

saveAsTextFile(path)

将DataSet当做TextFile进行写操作,在一个指定的Hadoop目录文件系统或者其它HDFS支持系统,Spark会调用toString将每个元素转换成一个文本文件

saveAsSequenceFile(path)

向一个给定的HDFS路径写入sequencefile格式文件,可以是任何Hadoop支持的文件系统。这是一种RDD任意有效的hadoop的Writeable接口形式键值对。scala中,它也是可以隐式转换成为可写类型(Spark包含的基本类型转换,如Int、Double、String等)

saveAsObectFile(path)

使用java序列化的一个数据集元素的格式简单书写形式,然后可以使用sparkContext.objectFile()加载.

countByKey()

只有在RDD类型(k,v),返回一个HashMap的(k,int)的每一个关键key的数量。

foreach(func)

运行在数据集的函数,用于每个元素的遍历。这是常用的形式,如更新累加器,或者配合外部存储系统使用

 

RDD的持久化

Spark最重要的能力之一就是将数据集持久化(或者缓存)在内存中的操作,当你持久化一个RDD任何节点或者分区都可以进行计算并重用它们(或者从它们中产生)这使得未来的Action执行要快得多(常常在10倍以上)在快速迭代算法和交互使用中,高速缓存是一个关键的工具。

你可以对RDD进行持久化标记是使用persist()或者cache()方法,第一次是在Action中计算,他将被保存在节点中的内存中。Spark的缓存容器是一种容错形式-如果任何RDD分区丢失了,它会自动重新计算使用最初创建的transformation结果。

另外,每个持久化RDD可以使用不同的存储层次,允许配置,例如,持久化在磁盘上,使用java序列化技术保存在内存中(节省空间)复制到节点,或者在堆中进行快速存储。这些级别通过一个StorageLevel 对象(scala、java、phthon)来persist()。该cache()方法是使用默认的存储级别来进行处理,这就是StorageLecel.MEMORY_ONLY(序列化对象存储在内存中)

 

MEMORY_ONLY

RDD作为java对象反序列化形式存储在JVM中。如果RDD不适合保存在内存中,默认情况下一些数据将不会被缓存而将被重新计算。

MEMORY_AND_DISK

RDD作为java对象序列化存储在JVM中,如果RDD不适合保存在内存中,将它储存在磁盘上,并在需要时候进行读取。

MEMORY_ONLY_SER

作为序列化的java对象存储(RDD作为每个分区的一个字节数组),这通常能够存储更多有效的数据,尤其是使用一个更快速的序列化程序,但增加了CPU的阅读数量。

MEMORY_AND_DISK_SER

类似MEMORY_ONLY_SER,只是将不适合存储在内存中的数据存储在磁盘上,而不是重新计算他们。

DISK_ONLY

RDD只存在磁盘分区中

MEMORY_ONLY_2

MEMORY_AND_DISK_2

etc

和上面水平相同,但是rryeplicate每个分片在两个集群节点上

OFF_HEAP

(experimental)

快速的序列化方式,对比MEMORY_ONLY_SER,OFF_HEAP堆存储可以进行reduce垃圾收集,允许较小的和共享内存池的环境是非常有吸引力的设计。此外,快速的RDD执行者不会失去存储在内存的缓存。在这种方法中,主存储器是最高效的,因此,不会试图将它从内存中剔除

 

注意:在Python中,存储的对象永远是Serialized和Pickle库操作的,所以它并不会关心你选择的是那个级别

 

还有一些Spark中间执时,节点行的自动shuffle工作(例如,reduceByKey)在没有用户调用时自动完成的,在节点故障时为了避免重新计算整个输入,我们还是建议用户调用RDD持久化数据并尽量重用它。

 

存储级别的选择

Spark的存储级别的目的是提供内存和CPU之间效率的不同取舍。我们建议通过以下几个方式来选择。

1、如果你的RDD使用默认的存储级别(MEMORY_ONLY)不要这样,这是一个有效的尽可能快的执行的选择。

2、如果没有,试着使用MEMORY_ONLY_SER并使用快速序列化使对象节约存储空间,但是依然能够快速的进行访问

3、不要能够使数据溢出到磁盘上面的方法,除非是非常重要的,或者需要用它们过滤大量数据。否则,在另一个分区上面重新计算可能和读取它们的速度一样快

4、如果你希望使用存储级别来进行快速的故障恢复(例如,利用Spark为从Web应用程序请求)所有的存储级别重新计算丢失的数据,提供完整的容错性,但是复制的数据没有RDD重新计算时候的等待

5、在大量内存或者多个应用程序环境中,OFF_HEAP模式具有以下优点

                 允许多个执行者快速在内存池里面进行共享

                 大大减少了垃圾收集的成本

                 缓存数据执行者崩溃时不会丢失

 

删除数据

Spark自动监视每个节点上锁使用的内存,将最近最少使用的旧的数据分区least-recently-used(LRU)清理掉,如果你想手动删除而不是等它的高速缓存自动清理,使用RDD.unpersist()方法

 

共享变量

通常,一个方法传递给操作在远程集群节点上的参数(类似map或者reduce),这对所有的方法来说是分离的。这些变量会被复制到每台机器,没有更新的变量在远程被传回driverManager,这样一来一般性读写和跨任务读写共享变量是很低效的。然而,Spark确实提供了两个常见的模式下的两个共享变量类型:广播变量(broadcast variables)和持久化变量(accumulators)

 

广播变量

广播变量允许程序员将只读变量缓存到每个机器上而不是远程读取。可以这样使用,例如,给每个节点用于拷贝大的数据集的一个有效的方式。Spark还试图使用广播变量使得广播算法降低通讯成本。

广播变量通过调用SparkContext.broadcast(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不应该被修改从而保证所有节点上的同一广播变量读取的值是相同的(例如,变量传递到一个新的节点后)

 

持久化变量

持久化变量是唯一的通过“添加”进行关联的操作变量,因此可以有效的支持并行变量。他们可以被用来实现计数器(类似map,reduce)或者计算总和。Spark支持数值类型的持久化变量,程序员可以添加新的类型支持。如果持久化变量在创建时有一个名字,那么它将被显示在Spark的界面上。这在运行阶段是十分有效的(注意:不支持Python)

持久化变量是通过调用SparkContext.accumulator(v)创建的。集群中运行的任务可以运行add方法中或者使用+=操作符(Scala和Python)来进行增量。然而,他们不能读取它的值。只有driver program可以调用方法读取持久化变量的值。

下面的代码显示了持久化变量作为一个数组的元素添加

 

scala>val accum=sc.accumulator(0,"my Accumulator")

accum:spark.Accumulator[Int]=0

 

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

...

10/09/2918:41:08 INFO SparkContext:Task finished in 0.317106 s

 

scala>accum.value

res2:Int=10

 

这段代码用于int类型的默认支持持久化变量创建,程序员也可以通过子类AccumulatorParam创建自己的类型。该AccumulatorParam接口有两个方法:zero()为您的数据类型提供了一个“零”值,并添加两个值相加方法addInPlace()。例如,假设我们有一个数字Vector,我们可以这样写:

Object VectorAccumulatorParam extends AccumulatorParam[Vector]{

                 defzero(initialValue:Vector):Vector ={

                                Vector.Zeros(initialValues.size)

                 }

                 defaddInPalace(v1:Vector,v2:Vector):Vector={

                                v1+=v2

                 }

}

 

//于是,创建一个这个类型的持久化变量

val vecAccum= sc.accumulator(new Vector(...))(VectorAccumulatorParam)

 

在scala中,Spark也支持一般的累加接口用于添加不同类型的元素使用(例如建立一个名单,收集一些元素)并且能使用SparkContext.accumulableCollection方法进行scala集合类型的累加。

 

单元测试

Spark的易于单元测试和使用任何常用的单元测试框架。简单的创建你的本地Master地址,运行你的操作,然后调用SparkContext.stop()停下来。确保你的context停止在一个finally block或者测试框架的断点上面,Spark不支持这两种东西同时运行。

 

从1.0迁移到现在的版本的变化

Spark1.0冻结了Spark1.X的核心API,任何没有被标记为“实验(experimental)”或者“开发者API(developer API)”中的东西将在未来的版本中支持。scala用户的唯一变化是分组操作,例如groupBykey,cogroup和join这个改变也适用于Spark的配套程序中比如Streaming,MLlib和Graph

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值