1 Spark介绍
行业正在广泛使用Hadoop来分析他们的数据集。 原因是Hadoop框架基于简单的编程模型(MapReduce),并且它支持可扩展,灵活,容错且经济高效的计算解决方案。 在这里,主要关注的是在查询之间的等待时间和运行程序的等待时间方面保持处理大型数据集的速度。
Spark由Apache软件基金会推出,用于加速Hadoop计算软件的计算过程。
与普遍的看法相反,Spark不是Hadoop的修改版本,并且实际上并不依赖于Hadoop,因为它拥有自己的集群管理。 Hadoop只是实现Spark的一种方式。
Spark使用Hadoop有两种方式,一种是存储,另一种是处理。 由于Spark有自己的集群管理计算,因此它仅将Hadoop用于存储目的。
Apache Spark
Apache Spark是一种闪电般的群集计算技术,专为快速计算而设计。 它基于Hadoop MapReduce,它扩展了MapReduce模型以便将其用于更多类型的计算,其中包括交互式查询和流处理。 Spark的主要特点是其内存集群计算,可提高应用程序的处理速度。
Spark旨在涵盖各种工作负载,如批处理应用程序,迭代算法,交互式查询和流式处理。 除了在各自的系统中支持所有这些工作负载之外,它还减少了维护单独工具的管理负担。
Apache Spark演化
Spark是Hadoop的子项目之一,2009年在加州大学伯克利分校的Matei Zaharia的AMPLab中开发。 它是在2010年根据BSD许可开放源代码。 它于2013年捐赠给Apache软件基金会,现在Apache Spark已经成为2014年2月以来的顶级Apache项目。
Apache Spark特性
1、Speed:Spark有助于在Hadoop集群中运行应用程序,内存速度提高了100倍,在磁盘上运行速度提高了10倍。 这可以通过减少对磁盘的读/写操作来实现。 它将中间处理数据存储在内存中。
2、支持多种语言:Spark提供Java,Scala或Python中的内置API。 因此,您可以使用不同的语言编写应用程序。 Spark使用80多种高效的操作算子进行交互式查询。
3、高级分析:Spark不仅支持'Map'和'reduce'。 它还支持SQL查询,流数据,机器学习(ML)和图算法。
Hadoop上构建Spark
下图显示了如何使用Hadoop组件构建Spark的三种方法。
如下所述,有三种Spark部署方式。
Standalone:Spark Standalone部署意味着Spark占用HDFS(Hadoop Distributed File System)之上的空间,并且显式地为HDFS分配空间。 在这里,Spark和MapReduce将并行运行,以涵盖集群上的所有Spark任务。
Hadoop Yarn:Hadoop Yarn部署意味着,只需在Yarn上运行即可,无需任何预安装或root访问权限。 它有助于将Spark集成到Hadoop生态系统或Hadoop堆栈中。 它允许其他组件在栈顶运行。
Spark in MapReduce(SIMR):除了独立部署之外,MapReduce中的Spark还用于启动Spark作业。 使用SIMR,用户可以启动Spark并使用其外壳,而无需任何管理访问。
Spark组件
下面的插图描述了Spark的不同组件。
Spark Core
Spark Core是Spark平台的基础通用执行引擎,其所有其他功能都是基于此平台的。 它提供了内存计算和外部存储系统中的参考数据集。
Spark SQL
Spark SQL是Spark Core上的一个组件,它引入了一个名为SchemaRDD的新的数据抽象,它提供了对结构化和半结构化数据的支持。
Spark Streaming
Spark Streaming利用Spark Core的快速调度功能执行流式分析。 它以小批量采集数据,并对这些小批量数据执行RDD(弹性分布式数据集)转换。
MLlib(机器学习库)
MLlib是Spark上面的分布式机器学习框架,因为它是基于分布式内存的Spark体系结构。根据基准测试,由MLlib开发人员针对交替最小二乘(ALS)实现完成。 Spark MLlib的速度是Apache Mahout的Hadoop磁盘版本的9倍(在Mahout获得Spark界面之前)。
GraphX
GraphX是一个位于Spark顶部的分布式图形处理框架。 它提供了一个用于表达图形计算的API,可以使用Pregel抽象API对用户定义的图形进行建模。 它还为此抽象提供了运行时的优化。
2 RDD
Resilient Distributed Datasets
弹性分布式数据集(RDD)是Spark的基础数据结构。它是一个不可变的分布式对象集合。 RDD中的每个数据集都被划分为逻辑分区,这些逻辑分区可以在集群的不同节点上进行计算。 RDD可以包含任何类型的Python,Java或Scala对象,包括用户定义的类。
形式上,RDD是一个只读分区的记录集合。可以通过对稳定存储或其他RDD上的数据进行确定性操作来创建RDD。 RDD是可以并行操作的容错元素集合。有两种创建RDD的方法:在驱动程序中并行化现有集合,或在外部存储系统中引用数据集,例如共享文件系统,HDFS,HBase或提供Hadoop输入格式的任何数据源。
Spark利用RDD的概念来实现更快更高效的MapReduce操作。让我们先讨论MapReduce操作如何发生以及为什么它们效率不高。
Data Sharing is Slow in MapReduce
MapReduce被广泛用于在集群上处理和生成具有并行分布式算法的大型数据集。 它允许用户使用一组高级操作员编写并行计算,而不必担心工作分配和容错。
不幸的是,在大多数当前的框架中,在计算之间重用数据的唯一方式(例如:在两个MapReduce作业之间)是将其写入外部稳定存储系统(例如:HDFS)。 虽然这个框架为访问集群的计算资源提供了许多抽象,但用户仍然需要更多。
迭代和交互式应用程序都需要在并行作业中更快速地共享数据。 由于复制,序列化和磁盘IO,MapReduce中的数据共享缓慢。 关于存储系统,大多数Hadoop应用程序,他们花费了90%以上的时间进行HDFS读写操作。
Iterative Operations on MapReduce(MapReduce上的迭代操作)
在多阶段应用程序中跨多个计算重用中间结果。 下图说明了当前框架如何工作,同时在MapReduce上执行迭代操作。 这由于数据复制,磁盘I/O和序列化而导致大量开销,这使得系统变慢。
用户对同一数据子集运行临时查询。 每个查询都将执行稳定存储上的磁盘I/O,这可以控制应用程序的执行时间。
下图说明了在MapReduce上进行交互式查询时当前框架的工作原理。
Data Sharing using Spark RDD
由于复制,序列化和磁盘IO,MapReduce中的数据共享缓慢。 大多数Hadoop应用程序中,他们花费了90%以上的时间进行HDFS读写操作。
认识到这个问题,研究人员开发了一个名为Apache Spark的专门框架。 Spark的关键思想是弹性分布式数据集(RDD); 它支持内存中的处理计算。 这意味着,它将内存状态作为对象存储在作业中,并且对象可在这些作业之间共享。 内存中的数据共享速度比网络和磁盘快10到100倍。
现在让我们试着找出Spark RDD中如何进行迭代和交互操作。
Iterative Operations on Spark RDD
下图给出了Spark RDD上的迭代操作。 它会将中间结果存储在分布式内存中,而不是稳定存储(Disk),并使系统更快。
注意:如果分布式内存(RAM)不足以存储中间结果(JOB的状态),则它会将这些结果存储在磁盘上。
此插图显示Spark RDD上的交互操作。 如果重复对同一组数据运行不同的查询,则可以将此特定数据保存在内存中以获得更好的执行时间。
默认情况下,每次对其执行操作时,每个转换后的RDD都可能会重新计算。 但是,您也可以将RDD保留在内存中,在这种情况下,Spark会保留群集中的元素,以便在下次查询时快速访问。 还支持在磁盘上保存RDD,或在多个节点上复制RDD。
3 安装
Spark是Hadoop的子项目。 因此,最好将Spark安装到基于Linux的系统中。 以下步骤显示如何安装Apache Spark。
1 验证Java安装
Java安装是安装Spark的强制性内容之一。 尝试以下命令来验证JAVA版本。
$java -version |
如果Java已经安装在您的系统上,您将看到以下响应 –
java version "1.7.0_71" Java(TM) SE Runtime Environment (build 1.7.0_71-b13) Java HotSpot(TM) Client VM (build 25.0-b02, mixed mode) |
如果您的系统上没有安装Java,请在继续下一步之前安装Java。
2 验证Scala版本
你应该使用Scala语言来实现Spark。 因此,让我们使用以下命令验证Scala安装。
$scala -version |
如果您的系统上已经安装了Scala,您会看到以下响应 –
Scala code runner version 2.11.6 -- Copyright 2002-2013, LAMP/EPFL |
如果您的系统上没有安装Scala,请继续下一步Scala安装。
3 下载Scala
通过访问以下链接下载最新版本的Scala下载Scala。 对于本教程,我们使用的是scala-2.11.6版本。 下载后,您会在下载文件夹中找到Scala tar文件。
4 安装Scala
按照以下给出的步骤安装Scala。
解压缩Scala tar文件
键入以下命令以提取Scala tar文件。
$ tar xvf scala-2.11.6.tgz |
移动Scala文件
使用以下命令将Scala软件文件移动到相应的目录(/usr/local/scala)。
$ su – Password: # cd /home/Hadoop/Downloads/ # mv scala-2.11.6 /usr/local/scala # exit |
设置Scala的PATH
使用以下命令为Scala设置PATH。
$ export PATH = $PATH:/usr/local/scala/bin |
验证Scala的安装
安装后,最好验证一下。 使用以下命令验证Scala安装。
$scala -version |
如果您的系统上已经安装了Scala,您会看到以下响应 –
Scala code runner version 2.11.6 -- Copyright 2002-2013, LAMP/EPFL |
5 下载Apache Spark
通过访问以下链接下载Spark,下载最新版本的Spark。 在本教程中,我们使用的是spark-1.3.1-bin-hadoop2.6版本。 下载完成后,您会在下载文件夹中找到Spark tar文件。
安装Spark
按照下面给出的步骤安装Spark。
解压Spark的tar文件。
$ tar xvf spark-1.3.1-bin-hadoop2.6.tgz |
移动Spark文件
以下用于将Spark软件文件移动到相应目录(/ usr / local / spark)的命令。
$ su – Password: # cd /home/Hadoop/Downloads/ # mv spark-1.3.1-bin-hadoop2.6 /usr/local/spark # exit |
设置Spark的环境变量
将以下行添加到〜/ .bashrc文件中。 这意味着将spark软件文件所在的位置添加到PATH变量中。
export PATH = $PATH:/usr/local/spark/bin |
使用以下命令获取~/.bashrc文件。
$ source ~/.bashrc |
6 验证Spark的安装
写下面的命令打开Spark shell。
$spark-shell |
如果spark安装成功,你会发现下面的输出。
Spark assembly has been built with Hive, including Datanucleus jars on classpath Using Spark's default log4j profile: org/apache/spark/log4j-defaults.properties 15/06/04 15:25:22 INFO SecurityManager: Changing view acls to: hadoop 15/06/04 15:25:22 INFO SecurityManager: Changing modify acls to: hadoop 15/06/04 15:25:22 INFO SecurityManager: SecurityManager: authentication disabled; ui acls disabled; users with view permissions: Set(hadoop); users with modify permissions: Set(hadoop) 15/06/04 15:25:22 INFO HttpServer: Starting HTTP Server 15/06/04 15:25:23 INFO Utils: Successfully started service 'HTTP class server' on port 43292. Welcome to ____ __ / __/__ ___ _____/ /__ _\ \/ _ \/ _ `/ __/ '_/ /___/ .__/\_,_/_/ /_/\_\ version 1.4.0 /_/ Using Scala version 2.10.4 (Java HotSpot(TM) 64-Bit Server VM, Java 1.7.0_71) Type in expressions to have them evaluated. Spark context available as sc scala> |
4 Spark Core编程
Spark Core是整个项目的基础。 它提供分布式任务调度,调度和基本I/O功能。 Spark使用称为RDD(弹性分布式数据集)的专用基础数据结构,它是跨计算机分区的逻辑数据集合。 RDD可以通过两种方式创建; 一种是通过引用外部存储系统中的数据集,另一种是通过在现有的RDD上应用变换(例如map,filter,reduce,join)。
RDD抽象是通过一个语言集成的API公开的。 这简化了编程复杂性,因为应用程序操作RDD的方式与操作本地数据集合类似。
1 Spark Shell
Spark提供了一个交互式shell:交互式分析数据的强大工具。 它以Scala或Python语言提供。 Spark的主要抽象是分布式的项目集合,称为弹性分布式数据集(RDD)。 可以从Hadoop输入格式(例如HDFS文件)或通过转换其他RDD创建RDD。
打开Spark Shell
以下命令用于打开Spark shell。
$ spark-shell |
创建简单的RDD
让我们从文本文件中创建一个简单的RDD。 使用以下命令创建一个简单的RDD。
scala> val inputfile = sc.textFile(“input.txt”) |
上述命令的输出是
inputfile: org.apache.spark.rdd.RDD[String] = input.txt MappedRDD[1] at textFile at <console>:12 |
Spark RDD API引入了一些转换和少量操作来操作RDD。
2 RDD Transformations
RDD转换返回指向新RDD的指针,并允许您创建RDD之间的依赖关系。 依赖链(依赖项字符串)中的每个RDD都有一个用于计算其数据的函数,并且具有指向其父RDD的指针(依赖项)。
Spark是懒惰的,所以除非你调用一些会触发作业创建和执行的转换或动作,否则什么也不会执行。 看下面的单词计数示例片段。
因此,RDD转换不是一组数据,而是程序中的一个步骤(可能是唯一的步骤),告诉Spark如何获取数据以及如何处理数据。
以下给出了RDD转换的列表。
Transformation | 含义 |
map(func) | 返回一个新的分布式数据集,通过传递函数func中的每个元素来形成。 |
filter(func) | 返回通过选择func返回true的源的那些元素形成的新数据集。 |
faltMap(func) | 与map类似,但是每个输入项可以映射到0个或更多的输出项(所以func应该返回一个Seq而不是单个项)。 |
mapPartitions(func) | 与map类似,但是在RDD的每个分区(块)上分别运行,当运行类型为T的RDD时,func必须是Iterator <T> => Iterator <U>类型。 |
mapPartitionsWithIndex(func) | 与map分区类似,但也为表示分区索引的整数值提供了func,所以当在T型RDD上运行时,func的类型必须是(Int,Iterator <T>)=> Iterator <U>。 |
Sample(withReplacement,fraction,seed) | 使用给定的随机数生成器种子对部分数据进行采样,包括或不包括替换。 |
union(otherDataset) | 返回包含源数据集中的元素和参数的联合的新数据集。 |
intersection(otherDataset) | 返回一个新的RDD,其中包含源数据集中的元素与参数的交集。 |
distinct([numTasks]) | 返回包含源数据集的不同元素的新数据集。 |
groupByKey([numTasks]) | 当调用(K,V)对的数据集时,返回(K,Iterable <V>)对的数据集。 注意:如果您正在进行分组以执行每个键的聚合(例如总和或平均),则使用reduceByKey或aggregateByKey会产生更好的性能。 |
reduceByKey(func,[numTasks]) | 在(K,V)对的数据集上调用时,返回(K,V)对的数据集,其中每个键的值使用给定的reduce函数func进行聚合,该函数必须是(V,V)=> V.与groupByKey一样,reduce任务的数量可通过可选的第二个参数进行配置。 |
aggregateByKey(zeroValue)(seqOp,combOp,[numTasks]) | 当调用(K,V)对的数据集时,返回(K,U)对的数据集,其中每个键的值使用给定的组合函数和中性“零”值进行聚合。 允许与输入值类型不同的聚合值类型,同时避免不必要的分配。 和groupByKey一样,reduce任务的数量可以通过可选的第二个参数来配置。 |
sortByKey([ascending],[numTasks]) | 当调用K实现Ordered的(K,V)对的数据集时,按照布尔上升参数中的指定,按照升序或降序顺序返回按键排序的(K,V)对的数据集。 |
join(otherDataset,[numTasks]) | 在类型(K,V)和(K,W)的数据集上调用时,返回包含每个键的所有元素对的(K,(V,W))对的数据集。 外连接通过leftOuterJoin,rightOuterJoin和fullOuterJoin支持。 |
cogroup(otherDataset,[numTasks]) | 在类型(K,V)和(K,W)的数据集上调用时,返回(K,(Iterable <V>,Iterable <W>))元组的数据集。这个操作也叫做Group With。 |
cartesian(otherDataset) | 当调用类型T和U的数据集时,返回(T,U)对(所有元素对)的数据集。 |
pip(command,[envVars]) | 通过shell命令管理RDD的每个分区,例如, 一个Perl或bash脚本。 RDD元素被写入进程的stdin,输出到stdout的行作为字符串的RDD返回。 |
coalesce(numPartitions) | 减少RDD中的分区数量为numPartitions。 用于过滤大型数据集后更高效地运行操作。 |
repartition(numPartitions) | 随机调整RDD中的数据以创建更多或更少的分区并在其间进行平衡。 这总是通过网络混洗所有数据。 |
repartitionAndSortWithinPartitions(partitioner) | 根据给定的分区程序对RDD进行重新分区,并在每个生成的分区中按键对记录进行排序。 这比调用重新分区,然后在每个分区内进行排序更有效率,因为它可以将排序压入洗牌机器。 |
3 Actions
下表给出了一个Actions列表以及它的返回值。
Action | 含义 |
reduce(func) | 使用函数func(它接受两个参数并返回一个)来聚合数据集的元素。 该函数应该是可交换和关联的,以便可以并行地正确计算它。 |
collect() | 在驱动程序中将数据集的所有元素作为数组返回。 在过滤器或其他操作返回足够小的数据子集之后,这通常很有用。 |
count() | 返回数据集中元素的数量。 |
first() | 返回数据集的第一个元素(类似于take(1))。 |
take(n) | 返回包含数据集前n个元素的数组。 |
takeSample(withReplacement,num,[seed]) | 返回一个数组,其中包含数据集的num元素的随机样本,包含或不包含替换,可以预先指定一个随机数生成器种子。 |
takeOrdered(n,[ordering]) | 使用自然顺序或自定义比较器返回RDD的前n个元素。 |
saveAsTextFile(path) | 将数据集的元素作为文本文件(或文本文件集)写入本地文件系统,HDFS或任何其他Hadoop支持的文件系统中的给定目录中。 Spark在每个元素上调用toString将其转换为文件中的一行文本。 |
saveAsSequenceFile(path)(Java and Scala) | 将数据集的元素作为Hadoop SequenceFile写入本地文件系统,HDFS或任何其他Hadoop支持的文件系统的给定路径中。 这在实现Hadoop的Writable接口的键值对的RDD上可用。 在Scala中,它也可用于可隐式转换为Writable的类型(Spark包含Int,Double,String等基本类型的转换)。 |
saveAsObjectFile(path)(Java and Scala) | 使用Java序列化以简单的格式写入数据集的元素,然后使用SparkContext.objectFile()加载该序列化。 |
countByKey() | 仅适用于类型(K,V)的RDD。 用(K,Int)对的hashmap返回每个键的计数。 |
foreach(func) | 在数据集的每个元素上运行函数func。 这通常是为了副作用而完成的,例如更新累加器或与外部存储系统交互。 注意:修改foreach()之外的累加器以外的变量可能会导致未定义的行为。 有关更多详细信息,请参阅了解闭包 |
4 RDD编程
借助示例,让我们看看RDD编程中几个RDD转换和操作的实现。
示例:
考虑一个字数统计的例子:它对出现在文档中的每个单词进行计数。 考虑将下列文本作为输入,并将其作为input.txt文件保存在主目录中。
input.txt: 输入文件
people are not as beautiful as they look, as they walk or as they talk. they are only as beautiful as they love, as they care as they share. |
按照下面给出的步骤执行给定的例子。
打开Shell。
以下命令用于打开Spark Shell。 一般来说,Spark是使用Scala构建的。 因此,Spark程序在Scala环境中运行。
$ spark-shell |
如果Spark shell成功打开,你会发现下面的输出。 查看输出的最后一行“Spark context available as sc”意味着Spark容器将自动创建名为sc的spark上下文对象。 在开始程序的第一步之前,应该创建SparkContext对象。
创建RDD
首先,我们必须使用Spark-Scala API读取输入文件并创建一个RDD。
以下命令用于从给定位置读取文件。 这里,使用inputfile的名称创建新的RDD。 在textFile("")方法中作为参数给出的字符串是输入文件名的绝对路径。 但是,如果只给出文件名,则表示输入文件位于当前位置。
scala> val inputfile = sc.textFile("input.txt") |
执行Word count Transformation
我们的目标是统计文件中的单词。 创建一个faltMap,将每一行分成单词(flatMap(line =>line.split(""))。
接下来,使用映射函数(map(word =>(word,1)))将每个单词作为值为'1'(<key,value> = <word,1>)的键读取。
最后,通过添加相似键的值(reduceByKey(_ + _))来减少这些键。
以下命令用于执行字数逻辑。 执行此操作后,您不会找到任何输出,因为这不是一个操作,这是一个转换; 指出一个新的RDD或者告诉spark如何处理给定的数据)
scala> val counts = inputfile.flatMap(line => line.split(" ")).map(word => (word, 1)).reduceByKey(_+_); |
当前的RDD
在使用RDD时,如果您想了解当前的RDD,请使用以下命令。 它将向您显示关于当前RDD及其调试依赖关系的描述。
scala> counts.toDebugString |
缓存RDD(Caching the Transformations)
您可以使用其上的persist()或cache()方法将RDD标记为持久化。 第一次在动作中计算时,它将保存在节点的内存中。 使用以下命令将中间转换存储在内存中。
scala> counts.cache() |
Applying the Action(应用操作)
应用一个动作,例如存储所有转换,结果到一个文本文件中。 saveAsTextFile("")方法的String参数是输出文件夹的绝对路径。 尝试使用以下命令将输出保存到文本文件中。 在以下示例中,“输出”文件夹位于当前位置。
scala> counts.saveAsTextFile("output") |
Checking the Output(检查输出)
打开另一个终端进入主目录(在另一个终端中执行spark)。 使用以下命令检查输出目录。
[hadoop@localhost ~]$ cd output/ [hadoop@localhost output]$ ls -1 part-00000 part-00001 _SUCCESS |
以下命令用于查看Part-00000文件的输出。
[hadoop@localhost output]$ cat part-00000 |
输出
(people,1) (are,2) (not,1) (as,8) (beautiful,2) (they, 7) (look,1) |
以下命令用于查看Part-00001文件的输出。
[hadoop@localhost output]$ cat part-00001 |
输出
(walk, 1) (or, 1) (talk, 1) (only, 1) (love, 1) (care, 1) (share, 1) |
UN持久化存储
在UN-Persistence之前,如果您想查看用于此应用程序的存储空间,请在浏览器中使用以下URL。
http://localhost:4040 |
您将看到以下屏幕,其中显示了在Spark shell上运行的应用程序使用的存储空间。
如果要不保留特定RDD的存储空间,请使用以下命令。
Scala> counts.unpersist() |
你会看到输出如下:
15/06/27 00:57:33 INFO ShuffledRDD: Removing RDD 9 from persistence list 15/06/27 00:57:33 INFO BlockManager: Removing RDD 9 15/06/27 00:57:33 INFO BlockManager: Removing block rdd_9_1 15/06/27 00:57:33 INFO MemoryStore: Block rdd_9_1 of size 480 dropped from memory (free 280061810) 15/06/27 00:57:33 INFO BlockManager: Removing block rdd_9_0 15/06/27 00:57:33 INFO MemoryStore: Block rdd_9_0 of size 296 dropped from memory (free 280062106) res7: cou.type = ShuffledRDD[9] at reduceByKey at <console>:14 |
要验证浏览器中的存储空间,请使用以下URL。
http://localhost:4040 |
5 Spark 部署
Spark应用程序(使用spark-submit)是用于在集群上部署Spark应用程序的shell命令。 它通过统一的界面使用所有相应的集群管理器。 因此,您不必为每个应用程序配置您的应用程序。
示例:
让我们使用之前使用的使用shell命令的单词计数的例子。 在这里,我们考虑与Spark应用程序相同的例子。
以下文本是输入数据,名为in.txt的文件。
people are not as beautiful as they look, as they walk or as they talk. they are only as beautiful as they love, as they care as they share. |
看看下面的程序:
SparkWordCount.scala
import org.apache.spark.SparkContext import org.apache.spark.SparkContext._ import org.apache.spark._ object SparkWordCount { def main(args: Array[String]) { val sc = new SparkContext( "local", "Word Count", "/usr/local/spark", Nil, Map(), Map()) /* local = master URL; Word Count = application name; */ /* /usr/local/spark = Spark Home; Nil = jars; Map = environment */ /* Map = variables to work nodes */ /*creating an inputRDD to read text file (in.txt) through Spark context*/ val input = sc.textFile("in.txt")
/* Transform the inputRDD into countRDD */ val count=input.flatMap(line=>line.split(" ")) .map(word=>(word, 1)) .reduceByKey(_ + _) /* saveAsTextFile method is an action that effects on the RDD */ count.saveAsTextFile("outfile") System.out.println("OK"); } } |
将上述程序保存到一个名为SparkWordCount.scala的文件中,并将其放在名为spark-application的用户定义目录中。
注意:在将inputRDD转换为countRDD时,我们使用flatMap()将行(从文本文件)标记为单词,使用map()方法计算单词频率,并使用reduceByKey()方法计算每个单词的重复次数。
使用以下步骤提交此应用程序。 通过终端执行spark-application目录中的所有步骤。
1 下载Spark jar
Spark Core jar是编译所必需的,因此,从下面的链接Spark core jar下载spark-core_2.10-1.3.0.jar并将jar文件从download目录下载到spark-application目录。
2 编译项目
使用下面给出的命令编译上述程序。 该命令应该从spark-application目录执行。 在这里,/usr/local/spark/lib/spark-assembly-1.4.0-hadoop2.6.0.jar是一个来自Spark库的Hadoop支持jar。
$ scalac -classpath "spark-core_2.10-1.3.0.jar:/usr/local/spark/lib/spark-assembly-1.4.0-hadoop2.6.0.jar" SparkPi.scala |
3 创建jar包
使用以下命令创建Spark应用程序的jar文件。 这里,wordcount是jar文件的文件名。
jar -cvf wordcount.jar SparkWordCount*.class spark-core_2.10-1.3.0.jar /usr/local/spark/lib/spark-assembly-1.4.0-hadoop2.6.0.jar |
4 提交Spark应用程序
使用以下命令提交spark应用程序:
spark-submit --class SparkWordCount --master local wordcount.jar |
如果它成功执行,那么你会发现下面给出的输出。 OK输入以下内容用于用户识别,这是程序的最后一行。 如果你仔细阅读下面的输出,你会发现不同的东西,比如:
successfully started service 'sparkDriver' on port 42954 MemoryStore started with capacity 267.3 MB Started SparkUI at http://192.168.1.217:4040 Added JAR file:/home/hadoop/piapplication/count.jar ResultStage 1 (saveAsTextFile at SparkPi.scala:11) finished in 0.566 s Stopped Spark web UI at http://192.168.1.217:4040 MemoryStore cleared |
15/07/08 13:56:04 INFO Slf4jLogger: Slf4jLogger started 15/07/08 13:56:04 INFO Utils: Successfully started service 'sparkDriver' on port 42954. 15/07/08 13:56:04 INFO Remoting: Remoting started; listening on addresses :[akka.tcp://sparkDriver@192.168.1.217:42954] 15/07/08 13:56:04 INFO MemoryStore: MemoryStore started with capacity 267.3 MB 15/07/08 13:56:05 INFO HttpServer: Starting HTTP Server 15/07/08 13:56:05 INFO Utils: Successfully started service 'HTTP file server' on port 56707. 15/07/08 13:56:06 INFO SparkUI: Started SparkUI at http://192.168.1.217:4040 15/07/08 13:56:07 INFO SparkContext: Added JAR file:/home/hadoop/piapplication/count.jar at http://192.168.1.217:56707/jars/count.jar with timestamp 1436343967029 15/07/08 13:56:11 INFO Executor: Adding file:/tmp/spark-45a07b83-42ed-42b3-b2c2-823d8d99c5af/userFiles-df4f4c20-a368-4cdd-a2a7-39ed45eb30cf/count.jar to class loader 15/07/08 13:56:11 INFO HadoopRDD: Input split: file:/home/hadoop/piapplication/in.txt:0+54 15/07/08 13:56:12 INFO Executor: Finished task 0.0 in stage 0.0 (TID 0). 2001 bytes result sent to driver (MapPartitionsRDD[5] at saveAsTextFile at SparkPi.scala:11), which is now runnable 15/07/08 13:56:12 INFO DAGScheduler: Submitting 1 missing tasks from ResultStage 1 (MapPartitionsRDD[5] at saveAsTextFile at SparkPi.scala:11) 15/07/08 13:56:13 INFO DAGScheduler: ResultStage 1 (saveAsTextFile at SparkPi.scala:11) finished in 0.566 s 15/07/08 13:56:13 INFO DAGScheduler: Job 0 finished: saveAsTextFile at SparkPi.scala:11, took 2.892996 s OK 15/07/08 13:56:13 INFO SparkContext: Invoking stop() from shutdown hook 15/07/08 13:56:13 INFO SparkUI: Stopped Spark web UI at http://192.168.1.217:4040 15/07/08 13:56:13 INFO DAGScheduler: Stopping DAGScheduler 15/07/08 13:56:14 INFO MapOutputTrackerMasterEndpoint: MapOutputTrackerMasterEndpoint stopped! 15/07/08 13:56:14 INFO Utils: path = /tmp/spark-45a07b83-42ed-42b3-b2c2-823d8d99c5af/blockmgr-ccdda9e3-24f6-491b-b509-3d15a9e05818, already present as root for deletion. 15/07/08 13:56:14 INFO MemoryStore: MemoryStore cleared 15/07/08 13:56:14 INFO BlockManager: BlockManager stopped 15/07/08 13:56:14 INFO BlockManagerMaster: BlockManagerMaster stopped 15/07/08 13:56:14 INFO SparkContext: Successfully stopped SparkContext 15/07/08 13:56:14 INFO Utils: Shutdown hook called 15/07/08 13:56:14 INFO Utils: Deleting directory /tmp/spark-45a07b83-42ed-42b3-b2c2-823d8d99c5af 15/07/08 13:56:14 INFO OutputCommitCoordinator$OutputCommitCoordinatorEndpoint: OutputCommitCoordinator stopped! |
5 Checking output
在成功执行程序后,您会在spark-application目录中找到名为outfile的目录。
以下命令用于打开和检查outfile目录中的文件列表。
$ cd outfile $ ls Part-00000 part-00001 _SUCCESS |
用于检查部分00000文件输出的命令是:
$ cat part-00000 (people,1) (are,2) (not,1) (as,8) (beautiful,2) (they, 7) (look,1) |
用于检查部分00001文件输出的命令是:
$ cat part-00001 (walk, 1) (or, 1) (talk, 1) (only, 1) (love, 1) (care, 1) (share, 1) |
阅读以下部分以更多地了解'spark-submit'命令。
Spark-submit语法
spark-submit [options] <app jar | python file> [app arguments] |
选项
下表给出了一个选项列表: -
选项 | 描述 |
--master | spark://host:port, mesos://host:port, yarn, 或者 local. |
--deploy-mode | 是在本地(“客户端”)还是在集群内的其中一台工作站计算机(“集群”)(默认:客户端)上启动驱动程序。 |
--class | 您的应用程序的主类(用于Java / Scala应用程序)。 |
--name | 您的应用程序的名称。 |
--jars | 包含在驱动程序和执行程序类路径中的逗号分隔的本地jar列表。 |
--packages | 包含在驱动程序和执行程序类路径中的jar的maven坐标的逗号分隔列表。 |
--repositories | 使用逗号分隔的附加远程存储库列表来搜索与--packages一起给出的maven坐标。 |
--py-files | 用逗号分隔的.zip,.egg或.py文件列表放置在Python应用程序的PYTHON PATH上。 |
--files | 逗号分隔的文件列表将放置在每个执行程序的工作目录中。 |
--conf(prop=val) | 任意Spark配置属性。 |
--properties-file | 从中加载额外属性的文件的路径。 如果未指定,则会查找conf / spark-defaults。 |
--driver-memory | 驱动程序内存(例如1000M,2G)(默认值:512M)。 |
--driver-java-options | 额外的Java选项传递给驱动程序。 |
--driver-library-path | 额外的库路径条目传递给驱动程序。 |
--driver-class-path | 额外的类路径条目传递给驱动程序。 请注意,添加了--jars的jar会自动包含在classpath中。 |
--executor-memory | 每执行器的内存(例如1000M,2G)(默认值:1G)。 |
--proxy-user | 用户在提交应用程序时模拟。 |
--help, -h | 显示此帮助信息并退出。 |
--verbose, -v | 打印额外的调试输出。 |
--version | 打印当前Spark的版本。 |
--driver-cores NUM | 驱动程序的内核(默认值:1)。 |
--supervise | 如果给出,则在失败时重新启动驱动程序。 |
--kill | 如果有,杀死指定的driver |
--status | 如果给出,请求指定的驱动程序的状态。 |
--total-executor-cores | 所有执行者的内核总数。 |
--executor-cores | 每个执行器的核心数量。 (默认值:在YARN模式下为1,或在独立模式下为worker上的所有可用内核)。 |
6 Spark高级编程
Spark包含两种不同类型的共享变量 - 一种是广播变量,另一种是累加器。
广播变量:用于高效分配大量值。
累加器:用于汇总特定收集的信息。
1 广播变量
广播变量允许程序员在每台机器上保存一个只读变量,而不是随任务一起发送它的副本。例如,可以使用它们以有效的方式为每个节点提供一个大型输入数据集的副本。 Spark还试图使用高效的广播算法来分发广播变量,以降低通信成本。
Spark动作通过一系列阶段执行,由分布式“shuffle”操作分隔。 Spark会自动广播每个Stage中任务所需的通用数据。
以这种方式广播的数据以序列化的形式被缓存,并在运行每个任务之前被反序列化。这意味着只有跨多个Stage的任务需要相同的数据或以反序列化形式缓存数据非常重要时,才显式创建广播变量。
广播变量是通过调用SparkContext.broadcast(v)从变量v创建的。广播变量是v的一个包装,它的值可以通过调用value方法来访问。下面给出的代码显示了这一点:
scala> val broadcastVar = sc.broadcast(Array(1, 2, 3)) |
输出:
broadcastVar: org.apache.spark.broadcast.Broadcast[Array[Int]] = Broadcast(0) |
在创建广播变量之后,应该使用它代替群集上运行的任何函数的值v,以便v不会多次传送到节点。 另外,为了确保所有节点获得与广播变量相同的值,对象v在广播后不应该被修改。
2 累加器
累加器是仅通过关联操作“添加”的变量,因此可以并行有效地支持。 它们可以用来实现计数(如在MapReduce中)或者求和。 Spark本身支持数字类型的累加器,程序员可以添加对新类型的支持。 如果累加器创建的时候有一个名称,那么它们将显示在Spark的UI中。 这对理解运行阶段的进度很有用(注意:Python尚未支持)。
通过调用SparkContext.accumulator(v)从初始值v创建累加器。 然后,可以使用add方法或+ =运算符(在Scala和Python中)添加在群集上运行的任务。 但是,它们无法读取这个值。 只有驱动程序可以使用其值方法读取累加器的值。
下面给出的代码显示了一个累加器,用于累加数组的元素:
scala> val accum = sc.accumulator(0) scala> sc.parallelize(Array(1, 2, 3, 4)).foreach(x => accum += x) |
如果您想查看上述代码的输出,请使用以下命令:
scala> accum.value |
输出:
res2: Int = 10 |
3 RDD数字操作
Spark允许您使用预定义的API方法之一对数字数据执行不同的操作。 Spark的数字操作是通过流式算法实现的,该算法允许构建模型,一次一个元素。
这些操作通过调用status()方法计算并作为StatusCounter对象返回。
以下是StatusCounter中可用的数字方法列表。
操作 | 含义 |
count() | RDD中元素的个数 |
Mean() | RDD中元素的平均值 |
Sum() | RDD中元素的和 |
Max() | RDD中元素的最大值 |
Min() | RDD中元素的最小值 |
Variance() | 元素的方差 |
Stdev() | 标准误差 |
如果您只想使用这些方法中的一种,则可以直接在RDD上调用相应的方法。