[譯]Spark快速開始

weixin_33860722發表於2017-04-18

本文是一個如何使用Spark的簡要教程。首先通過Spark的互動式Shell來介紹API(使用Python或Scala),然後展示如何用Java,Scala和Python來寫Spark引用程式。更完整的內容請參考程式設計指南

為了跟隨這篇指南進行學習,請首先從Spark網站上下載一個Spark release包。因為我們這裡不會使用HDFS,所以下載一個用於任意Hadoop版本的包即可。

使用Spark Shell進行互動式分析

基礎

Spark的Shell提供了一種學習API的簡單方法,同樣也是互動式分析資料的強大工具。在Scala(執行在Java虛擬機器上,可以使用現有的Java庫)或Python中可用。在Spark目錄下執行下面命令:

./bin/spark-shell

Spark的主要抽象是彈性分散式資料集(RDD)。RDD可以通過Hadoop輸入格式(比如HDFS檔案)或者其它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 <console>:25

RDD有可以返回值的actions,有可以返回指向新RDD指標的transformations。讓我們從幾個actions開始:

scala> textFile.count() // Number of items in this RDD
res0: Long = 126 // May be different from yours as README.md will change over time, similar to other outputs

scala> textFile.first() // First item in this RDD
res1: String = # Apache Spark

接下來使用transformation。我們使用filter transformation來返回一個包含檔案條目子集的RDD。

scala> val linesWithSpark = textFile.filter(line => line.contains("Spark"))
linesWithSpark: org.apache.spark.rdd.RDD[String] = MapPartitionsRDD[2] at filter at <console>:27

可以把transformations和actions連結起來使用。

scala> textFile.filter(line => line.contains("Spark")).count() // How many lines contain "Spark"?
res3: Long = 15

更多RDD操作

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函式找出最大行的單詞數。mapreduce的引數是Scala函式式(閉包),並且可以使用任何語言特性或者Scala/Java庫。例如,可以很容易地呼叫宣告在其它地方的函式。可以使用Math.max()來讓這段程式碼更好理解。

scala> import java.lang.Math
import java.lang.Math

scala> textFile.map(line => line.split(" ").size).reduce((a, b) => Math.max(a, b))
res5: Int = 15

一種常見的資料流模式為MapReduce,是由Hadoop推廣的。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 <console>:28

這裡我們結合了flatmapmapreduceByKey轉換來計算檔案中每個單詞的數量,生成一個(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 <console>:27

scala> linesWithSpark.count()
res8: Long = 15

scala> linesWithSpark.count()
res9: Long = 15

用Spark來探索和快取一個100行的文字檔案看起來有點呆。有意思的是這些相同的函式可以用在非常龐大的資料集上,即使跨越數十或者數百個節點。你也可以像程式設計指南中描述的一樣通過使用bin/spark-shell連線到叢集的方式,來互動式地做這件事。

獨立的應用程式

假設我們想用Spark API寫一個獨立的應用程式。可以使用Scala(with sbt),Java(with Maven)和Python實現一個簡單的應用程式。

在Scala中建立一個簡單的Spark應用程式,命名為SimpleApp.scala

/* SimpleApp.scala */
import org.apache.spark.SparkContext
import org.apache.spark.SparkContext._
import org.apache.spark.SparkConf

object 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的行數。注意把YOUR_SPARK_HOME替換成Spark的安裝路徑。不像之前使用Spark Shell的例子會初始化自己的SparkContext,我們需要初始化SparkContext作為程式的一部分。

我們要傳一個包含了應用程式資訊的SparkConf物件給SparkContext建構函式。

應用程式需要依賴Spark API,所以會包含一個sbt配置檔案,simple.sbt,裡面說明了Spark是一個依賴。這個檔案也新增了一個Spark依賴的庫:

name := "Simple Project"

version := "1.0"

scalaVersion := "2.11.7"

libraryDependencies += "org.apache.spark" %% "spark-core" % "2.1.0"

為了讓sbt正確執行,我們需要根據經典的目錄結構來組織 SimpleApp.scalasimple.sbt。完成之後,我們可以建立一個包含我們應用程式程式碼的JAR包,然後使用spark-submit指令碼來執行我們的程式。

# Your directory layout should look like this
$ find .
.
./simple.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應用程式!

  • 檢視API的深入概述,從Spark程式設計指南開始,或者看其它元件的程式設計指南選單。
  • 要在叢集上執行應用程式,請看部署概述
  • 最後,Spark在examples目錄中包含了很多示例(ScalaJavaPythonR),可以使用如下方式執行:
# 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

相關文章