本教程提供了使用Spark的快速介绍。我们将首先通过Spark的交互式Shell(使用Python或Scala)介绍API,然后说明如何使用Java,Scala和Python编写应用程序。请参阅编程指南以获取更完整的参考。
要遵循本指南,请先从Spark网站下载Spark的打包版本。由于我们不会使用HDFS,因此您可以下载适用于任何Hadoop版本的软件包。
Spark的Shell提供了一种简单的学习API的方法,以及一个功能强大的工具,可以以交互方式分析数据。它可以在Scala(可在Java VM上运行,因此可以很好的使用现有Java类库)或Python中提供。通过在Spark目录中运行以下命令来启动它:(这里只演示scala语言的案例,官网中有Pyton语言的案例)
./bin/spark-shell
Spark的主要抽象是称为“弹性分布式数据集(RDD)”的项目的分布式集合。可以从Hadoop InputFormats(例如HDFS文件)或通过转换其他RDD创建RDD。让我们从Spark源目录中的README文件的文本中创建一个新的RDD:
scala> val textFile = sc.textFile("README.md")textFile: org.apache.spark.rdd.RDD[String] = README.md MapPartitionsRDD[1] at textFile at :25
RDD具有actions,这些操作具有返回值,而transformations则返回指向新RDD的指针。让我们从一些actions开始:
scala> textFile.count() // Number of items in this RDDres0: Long = 126 // May be different from yours as README.md will change over time, similar to other outputsscala> textFile.first() // First item in this RDDres1: String = # Apache Spark
现在,让我们使用一个transformation。我们将使用filter转换来返回一个新的RDD,其中包含文件中项的子集。
scala> val linesWithSpark = textFile.filter(line => line.contains("Spark"))linesWithSpark: org.apache.spark.rdd.RDD[String] = MapPartitionsRDD[2] at filter at :27
我们可以将transformations和actions链接在一起:
scala> textFile.filter(line => line.contains("Spark")).count() // How many lines contain "Spark"?res3: Long = 15
RDD的actions和transformations可用于更复杂的计算。假设我们要查找包含最多单词的行:
scala> textFile.map(line => line.split(" ").size).reduce((a, b) => if (a > b) a else b)res4: Long = 15
首先,map将一行拆分到整数值(一行中有多少个用空格分隔的单词),以创建新的RDD。在该RDD上调用reduce以找到map中返回的整数中最大的行数。map和reduce的参数是Scala函数文字(闭包),可以使用任何语言功能如Scala/Java库。例如,我们可以轻松地调用在其他地方声明的函数。我们将使用Math.max()函数使得代码更容易理解:
scala> import java.lang.Mathimport java.lang.Mathscala> textFile.map(line => line.split(" ").size).reduce((a, b) => Math.max(a, b))res5: Int = 15
一种常见的数据流模式是Hadoop流行的MapReduce。 Spark可以轻松实现MapReduce流:
scala> val wordCounts = textFile.flatMap(line => line.split(" ")).map(word => (word, 1)).reduceByKey((a, b) => a + b)wordCounts: org.apache.spark.rdd.RDD[(String, Int)] = ShuffledRDD[8] at reduceByKey at :28
在这里,我们结合了flatMap,map和reduceByKey类型的transformations来计算文件中每个字的计数,作为(String,Int)对的RDD。要在我们的shell收集的单词的个数,我们可以使用collect action:
scala> wordCounts.collect()res6: Array[(String, Int)] = Array((means,1), (under,2), (this,3), (Because,1), (Python,2), (agree,1), (cluster.,1), ...)
Spark还支持在集群范围的内存的缓存中拉取数据集。当重复访问数据时,例如查询小的“热”数据集或运行迭代算法(如PageRank)时,这非常有用。举一个简单的例子,让我们将linesWithSpark数据集标记为要缓存的:
scala> linesWithSpark.cache()res7: linesWithSpark.type = MapPartitionsRDD[2] at filter at :27scala> linesWithSpark.count()res8: Long = 15scala> linesWithSpark.count()res9: Long = 15
使用Spark浏览和缓存100行文本文件似乎很愚蠢。有趣的是,即使在数十个或数百个节点之间进行条带化时,这些相同的函数也可以用于非常大的数据集。您还可以按照编程指南中的说明,通过将bin/spark-shell连接到集群来交互式地执行此操作。
假设我们希望使用Spark API编写一个独立的应用程序。我们将逐步介绍Scala(带有sbt),Java(带有Maven)和Python的简单应用程序。
我们将在Scala中创建一个非常简单的Spark应用程序-实际上如此简单,因此它被命名为SimpleApp.scala:
/* SimpleApp.scala */import org.apache.spark.SparkContextimport org.apache.spark.SparkContext._import org.apache.spark.SparkConfobject SimpleApp { def main(args: Array[String]) { val logFile = "YOUR_SPARK_HOME/README.md" // Should be some file on your system val conf = new SparkConf().setAppName("Simple Application") val sc = new SparkContext(conf) val logData = sc.textFile(logFile, 2).cache() val numAs = logData.filter(line => line.contains("a")).count() val numBs = logData.filter(line => line.contains("b")).count() println(s"Lines with a: $numAs, Lines with b: $numBs") sc.stop() }}
请注意,应用程序应定义main()方法,而不是扩展scala.App。 scala.App的子类可能无法正常工作。
该程序只计算Spark README文件中包含“a”的行数和包含“b”的行数。请注意,您需要用安装Spark的位置替换YOURSPARKHOME。与之前带有Spark shell的示例(初始化了自己的SparkContext)不同,我们将SparkContext初始化为程序的一部分。
我们向SparkContext构造函数传递一个SparkConf对象,该对象包含有关我们的应用程序的信息。
我们的应用程序依赖于Spark API,因此我们还将包含一个sbt配置文件build.sbt,该文件说明Spark是依赖项。该文件还添加了Spark依赖的存储库:
name := "Simple Project"
version := "1.0"
scalaVersion := "2.11.7"
libraryDependencies += "org.apache.spark" %% "spark-core" % "2.1.1"
为了使sbt正常工作,我们需要根据典型的目录结构对SimpleApp.scala和build.sbt进行布局。安装好之后,我们可以创建一个包含应用程序代码的JAR程序包,然后使用spark-submit脚本运行我们的程序。
# Your directory layout should look like this$ find .../build.sbt./src./src/main./src/main/scala./src/main/scala/SimpleApp.scala# Package a jar containing your application$ sbt package...[info] Packaging {..}/{..}/target/scala-2.11/simple-project_2.11-1.0.jar# Use spark-submit to run your application$ YOUR_SPARK_HOME/bin/spark-submit \ --class "SimpleApp" \ --master local[4] \ target/scala-2.11/simple-project_2.11-1.0.jar...Lines with a: 46, Lines with b: 23
祝贺您运行第一个Spark应用程序!
# For Scala and Java, use run-example:./bin/run-example SparkPi# For Python examples, use spark-submit directly:./bin/spark-submit examples/src/main/python/pi.py# For R examples, use spark-submit directly:./bin/spark-submit examples/src/main/r/dataframe.R