大資料基礎學習-9.Spark2.1.1

閒人勿-發表於2018-04-29

一、Spark基礎

參考:http://www.cnblogs.com/tgzhu/p/5818374.html

1.Spark簡介

Spark是一個快速的,通用的,大資料規模的運算引擎。2009年誕生於加州大學伯克利分校AMPLab2010年開源,2013年6月成為Apache孵化專案,20142月成為Apache頂級專案,用 Scala 進行編寫。

Spark是基於MapReduce實現的通用的分散式計算框架,繼承了MapReduce的優點,同時還支援將Job運算任務產生的中間結果和最終結果儲存在記憶體中,而不像MR放在磁碟,對於迭代運算效率更高,運算速度快。通常當需要處理的資料量超過了單機尺度(比如我們的計算機有4GB的記憶體,而我們需要處理100GB以上的資料)這時我們可以選擇spark叢集進行計算,有時我們可能需要處理的資料量並不大,但是計算很複雜,需要大量的時間,這時我們也可以選擇利用spark叢集強大的計算資源,並行化地計算。

目前,Spark生態系統已經發展成為一個包含多個子專案的集合,其中包含SparkSQLSpark StreamingGraphXMLibSparkR等子專案。Spark是基於記憶體計算的大資料平行計算框架,除了擴充套件MapReduce 計算模型,還高效地支援更多的計算模式,包括互動式查詢和流處理。Spark 適用於原先需要多種分散式平臺的場景,包括批處理、迭代演算法、互動式查詢、流處理。通過在一個統一的框架下支援這些不同的計算,簡單而低耗地把各種處理流程整合在一起,大大減輕了原先需要對各種平臺分別管理的負擔。 spark這種大一統的軟體棧,各個元件關係密切並且可以相互呼叫, 這種設計有幾個好處:1、 軟體棧中所有的程式庫和高階元件 都可以從下層的改進中獲益。 2、執行整個軟體棧的代價變小了。不需要執行510套獨立的軟體系統了,一個機構只需要執行一套軟體系統即可。系統的部署、維護、測試、支援等大大縮減。3、能夠構建出無縫整合不同處理模型的應用。

Spark Core實現了Spark的基本功能,包含任務排程、記憶體管理、錯誤恢復、與儲存系統互動等模組。Spark Core中還包含了對彈性分散式資料集(resilient distributed dataset,簡稱 RDD)API定義。

Spark SQLSpark用來操作結構化資料的程式包。通過Spark SQL,我們可以使用SQL或者Apache Hive版本的SQL方言(HQL)來查詢資料。SparkSQL支援多種資料來源,比 如Hive表、Parquet以及JSON等。

Spark StreamingSpark提供的對實時資料進行流式計算的元件。提供了用來運算元據流的API,並且與Spark Core中的 RDD API高度對應。

Spark MLlib提供常見的機器學習(ML)功能的程式庫。包括分類、迴歸、聚類、協同過濾等,還提供了模型評估、資料匯入等額外的支援功能。

GraphX:控制圖、並行圖操作和計算的一組演算法和工具的集合。GraphX擴充套件了RDD API,包含控制圖、建立子圖、訪問路徑上所有頂點的操作。

本篇部落格主要介紹spark的基礎概念、sparkcore和RDDs。對於sparksql和sparkstreaming將會直接通過專案進行學習。

2.Spark特點

快 :HadoopMapReduce相比,Spark基於記憶體的運算要快100倍以上,基於硬碟的運算也要快10倍以上。Spark 實現了高效的DAG執行引擎,可以通過基於記憶體來高效處理資料流。計算的中間結果是存在於記憶體中的。

易用:Spark支援JavaPythonScalaAPI,還支援超過80種高階演算法,使使用者可以快速構建不同的應用。而且 Spark 支援互動式的 Python Scala shell,可以非常方便地在這些 shell 中使用 Spark 叢集來驗證解決問題的方法。

通用:Spark提供了統一的解決方案。Spark可以用於批處理、互動式查詢(Spark SQL)、實時流處理(Spark Streaming)、機器學習(Spark MLlib)和圖計算(GraphX)。這些不同型別的處理都可以在同一個應用中無縫使用。Spark統一的解決方案非常具有吸引力,畢竟任何公司都想用統一的平臺去處理遇到的問題,減少開發和維護的人力成本和部署平臺的物力成本。

相容性:Spark可以非常方便地與其他的開源產品進行融合。比如,Spark可以使用HadoopYARNApache Mesos作為它的資源管理和排程器,並且可以處理所有Hadoop支援的資料,包括HDFSHBaseCassandra等。這對於已經部署Hadoop叢集的使用者特別重要,因為不需要做任何資料遷移就可以使用Spark 的強大處理能力。Spark也可以不依賴於第三方的資源管理和排程器,實現了Standalone作為其內建的資源管理和排程框架,這樣進一步降低了Spark的使用門檻,使得所有人都可以非常容易地部署和使用Spark

二、Spark架構

Cluster Manager:在standalone模式中為Master節點,控制整個叢集,監控worker;在YARN模式中為資源管理器RM。

Worker節點:從節點,負責控制計算節點,啟動Executor或者Driver。

Driver Program:執行Application的main()函式

Executor:執行器,是為某個Application執行在worker node上的一個程式。

1.Spark執行流程

1)構建Spark Application的執行環境,啟動SparkContext。在SparkContext初始化過程中,Spark分別建立作業排程模組DAGScheduler和任務排程模組TaskScheduler(此例為Standalone模式下,在YARN-Client模式下任務排程模組為YarnClientClusterScheduler,在YARN-Cluster模式下為YarnClusterScheduler)。

2)SparkContext向資源管理器(可以是Standalone,Mesos,Yarn)申請執行Executor資源,並啟動Standalone Executor backend,這裡的executor主要是程式。

3Executor向SparkContext申請Task。

4SparkContext將應用程式分發給Executor。

5SparkContext根據程式碼構建成DAG圖,將DAG圖分解成Stage、將Taskset傳送給Task Scheduler,最後由Task Scheduler將Task傳送給Executor執行。

6Task在Executor上執行,執行完釋放所有資源。

Spark執行特點:

1每個Application獲取專屬的executor程式,該程式在Application期間一直駐留,並以多執行緒方式執行Task。這種Application隔離機制是有優勢的,無論是從排程角度看(每個Driver排程他自己的任務),還是從執行角度看(來自不同Application的Task執行在不同JVM中),當然這樣意味著Spark Application不能跨應用程式共享資料,除非將資料寫入外部儲存系統。

2Spark與資源管理器無關,只要能夠獲取executor程式,並能保持相互通訊就可以了。

3提交SparkContext的Client應該靠近Worker節點(執行Executor的節點),最好是在同一個Rack裡,因為Spark Application執行過程中SparkContext和Executor之間有大量的資訊交換。

4Task採用了資料本地性和推測執行的優化機制,即task最好分配在資料所在的節點上,同時任務只有在觸發action操作時,才會真正執行,這在後面將會講解。

2.Spark常用術語

Application: Appliction都是指使用者編寫的Spark應用程式,其中包括一個Driver功能的程式碼和分佈在叢集中多個節點上執行的Executor程式碼。

Driver: Spark中的Driver即執行上述Application的main函式並建立SparkContext,建立SparkContext的目的是為了準備Spark應用程式的執行環境,在Spark中有SparkContext負責與ClusterManager通訊,進行資源申請、任務的分配和監控等,當Executor部分執行完畢後,Driver同時負責將SparkContext關閉,通常用SparkContext代表Driver。

Executor: 某個Application執行在worker節點上的一個程式,該程式負責執行某些Task,並且負責將資料存到記憶體或磁碟上,每個Application都有各自獨立的一批Executor,在Spark on Yarn模式下,其程式名稱為CoarseGrainedExecutorBackend。一個CoarseGrainedExecutorBackend有且僅有一個Executor物件, 負責將Task包裝成taskRunner,並從執行緒池中抽取一個空閒執行緒執行Task,每一個CoarseGrainedExecutorBackend並行執行的Task數量取決與分配給它的cpu個數。

Cluter Manager:指的是在叢集上獲取資源的外部服務。目前有三種型別:

    Standalone : spark原生的資源管理,由Master負責資源的分配

    Apache Mesos:與hadoop MR相容性良好的一種資源排程框架

    Hadoop Yarn: 主要是指Yarn中的ResourceManager

Worker: 叢集中任何可以執行Application程式碼的節點,在Standalone模式中指的是通過slave檔案配置的Worker節點,在Spark on Yarn模式下就是NodeManager節點。

Task: 被送到某個Executor上的工作單元,和hadoopMR中的MapTask和ReduceTask概念一樣,是執行Application的基本單位,多個Task組成一個Stage,而Task的排程和管理等是由TaskScheduler負責。

Job: 包含多個Task組成的平行計算,往往由Spark Action觸發生成,一個Application中往往會產生多個Job。

Stage: 每個Job會被拆分成多組Task,作為TaskSet, 其名稱為Stage,Stage的劃分和排程是有DAGScheduler來負責的,Stage有非最終的Stage(Shuffle Map Stage)和最終的Stage(Result Stage)兩種,Stage的邊界就是發生shuffle的地方。

DAGScheduler:根據Job構建基於Stage的DAG(Directed Acyclic Graph有向無環圖),並提交Stage給TASkScheduler。 其劃分Stage的依據是RDD之間的依賴的關係找出開銷最小的排程方法。

TASKSedulter: 將TaskSET提交給worker執行,每個Executor執行什麼Task就是在此處分配的。TaskScheduler維護所有TaskSet,當Executor向Driver發生心跳時,TaskScheduler會根據資源剩餘情況分配相應的Task。另外TaskScheduler還維護著所有Task的執行標籤,重試失敗的Task。在不同執行模式中任務排程器具體為:

    Spark on Standalone模式為TaskScheduler

    YARN-Client模式為YarnClientClusterScheduler

    YARN-Cluster模式為YarnClusterScheduler

總結如下圖。


Job=多個stage,Stage=多個同種task, Task分為ShuffleMapTask和ResultTask,Dependency分為ShuffleDependency和NarrowDependency。

三、Spark編譯和安裝

在編譯前,首先安裝maven和scala【本人安裝的版本是maven3.3.9和scala2.11】。maven和scala下載解壓後,配置環境變數即可使用。同時要保證java在7+版本以上。注意:如果Scala版本低於2.11,需要特別指定,如下。

./dev/change-scala-version.sh 2.10  #解壓spark原始碼包後,進入解壓得到的資料夾,在該目錄下執行

從官網上下載spark原始碼,這裡下載的是spark-2.1.1.tgz。【如果不想編譯可以直接下載相對應的安裝包,spark-2.1.1-bin-hadoop2.6.tgz】解壓原始碼包,進入該資料夾,首先檢視pom.xml的引數設定,下面只列出兩個引數,目的是用來講解編譯的命令。預設的引數如下。

  <properties>
    <java.version>1.7</java.version> #這裡可以看到對應的版本資訊
    <maven.version>3.3.9</maven.version>
    <hadoop.version>2.2.0</hadoop.version>
    <protobuf.version>2.5.0</protobuf.version>
    <yarn.version>${hadoop.version}</yarn.version>
  </properties>
    <profile>
      <id>hadoop-2.3</id>
      <properties>
        <hadoop.version>2.3.0</hadoop.version>
        <jets3t.version>0.9.3</jets3t.version>
      </properties>
    </profile>

pom.xml中可以看到spark原始碼所對應hadoop、yarn等版本的具體資訊,但是安裝的hadoop版本可能是不一樣的(例如本人安裝的就是hadoop-2.6.0-cdh5.7.0),所以需在編譯命令中指定。在執行編譯命令前,需要修改一下pom.xml,加入如下的內容,目的是為了能夠下載到編譯需要的依賴包。

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

修改pom.xml後,官網建議先執行下面的命令,為maven編譯提供足夠多的資源,否則容易出現記憶體不足的錯誤。

[root@master spark-2.1.0]# export MAVEN_OPTS="-Xmx2g -XX:ReservedCodeCacheSize=512m" 
[root@master spark-2.1.0]# ./build/mvn -Pyarn -Phadoop2.6 -Phive -Phive-thriftserver -Dhadoop.version=2.6.0-cdh5.7.0 -DskipTests clean package #編譯命令,同時使spark支援hive

或者採用如下的指令碼命令,這個命令編譯之後可以生成一個部署包:spark-$VERSION-bin-$NAME.tgz。

[root@masterspark-2.1.0]# ./dev/make-distribution.sh --name 2.6.0-cdh5.7.0 --tgz -Pyarn -Phadoop2.6 -Phive -Phive-thriftserver -Dhadoop.version=2.6.0-cdh5.7.0

其中:-P表示對profile的內容做修改,-D表示對properties的內容做修改,如果命令尾部接“+X”則可以看到更詳細的編譯資訊。具體參考官方文件:http://spark.apache.org/docs/latest/building-spark.html。編譯完之後就會在原始碼的目錄下生成安裝包,就可以解壓安裝。

四、spark幾種模式搭建

目前Apache Spark主要支援三種分散式部署方式,分別是standalone、spark on mesos和 spark on YARN【另外還有local模式,用於測試程式用】。其中,第一種類似於MapReduce 1.0所採用的模式,內部實現了容錯性和資源管理,後兩種則是未來發展的趨勢,部分容錯性和資源管理交由統一的資源管理系統完成:讓Spark執行在一個通用的資源管理系統之上,這樣可以與其他計算框架,比如MapReduce,公用一個叢集資源,最大的好處是降低運維成本和提高資源利用率(資源按需分配)。三種部署方式,優缺點如下。

參考文件:http://dongxicheng.org/framework-on-yarn/apache-spark-comparing-three-deploying-ways/

standalone模式,即獨立模式,自帶完整的服務,可單獨部署到一個叢集中,無需依賴任何其他資源管理系統。從一定程度上說,該模式是其他兩種的基礎。目前Spark在standalone模式下是沒有任何單點故障問題的,這是藉助zookeeper實現的,思想類似於Hbase master單點故障解決方案。將Spark standalone與MapReduce比較,會發現它們兩個在架構上是完全一致的:

1都是由master/slaves服務組成的,且起初master均存在單點故障,後來均通過zookeeper解決;

2各個節點上的資源被抽象成粗粒度的slot,有多少slot就能同時執行多少task。不同的是,MapReduce將slot分為map slot和reduce slot,它們分別只能供Map Task和Reduce Task使用,而不能共享,這是MapReduce資源利率低效的原因之一,而Spark則更優化一些,它不區分slot型別,只有一種slot,可以供各種型別的Task使用,這種方式可以提高資源利用率,但是不夠靈活,不能為不同型別的Task定製slot資源。


該模式主要的節點有Client節點、Master節點和Worker節點。其中Driver既可以執行在Master節點上中,也可以執行在本地Client端。當用spark-shell互動式工具提交Spark的Job時,Driver在Master節點上執行;當使用spark-submit工具提交Job或者在Eclips、IDEA等開發平臺上使用”new SparkConf.setManager(“spark://master:7077”)”方式執行Spark任務時,Driver是執行在本地Client端上的。具體執行流程為:

1)SparkContext連線到Master,向Master註冊並申請資源(CPU Core 和Memory);

2)Master根據SparkContext的資源申請要求和Worker心跳週期內報告的資訊決定在哪個Worker上分配資源,然後在該Worker上獲取資源,然後啟動StandaloneExecutorBackend;

3)StandaloneExecutorBackend向SparkContext註冊;

4)SparkContext將Applicaiton程式碼傳送給StandaloneExecutorBackend;並且SparkContext解析Applicaiton程式碼,構建DAG圖,並提交給DAG Scheduler分解成Stage(當碰到Action操作時,就會催生Job;每個Job中含有1個或多個Stage,Stage一般在獲取外部資料和shuffle之前產生),然後以Stage(或者稱為TaskSet)提交給Task Scheduler,Task Scheduler負責將Task分配到相應的Worker,最後提交給StandaloneExecutorBackend執行;

5)StandaloneExecutorBackend會建立Executor執行緒池,開始執行Task,並向SparkContext報告,直至Task完成;

6)所有Task完成後,SparkContext向Master登出,釋放資源。

Spark On Mesos模式。這是很多公司採用的模式,官方推薦這種模式(當然,原因之一是血緣關係)。正是由於Spark開發之初就考慮到支援Mesos,因此,目前而言,Spark執行在Mesos上會比執行在YARN上更加靈活,更加自然。目前在Spark On Mesos環境中,使用者可選擇兩種排程模式之一執行自己的應用程式:

1)粗粒度模式(Coarse-grained Mode):每個應用程式的執行環境由一個Dirver和若干個Executor組成,其中,每個Executor佔用若干資源,內部可執行多個Task(對應多少個“slot”)。應用程式的各個任務正式執行之前,需要將執行環境中的資源全部申請好,且執行過程中要一直佔用這些資源,即使不用,最後程式執行結束後,回收這些資源。舉個例子,比如你提交應用程式時,指定使用5個executor執行你的應用程式,每個executor佔用5GB記憶體和5個CPU,每個executor內部設定了5個slot,則Mesos需要先為executor分配資源並啟動它們,之後開始排程任務。另外,在程式執行過程中,mesos的master和slave並不知道executor內部各個task的執行情況,executor直接將任務狀態通過內部的通訊機制彙報給Driver,從一定程度上可以認為,每個應用程式利用mesos搭建了一個虛擬叢集自己使用。

2)細粒度模式(Fine-grained Mode):鑑於粗粒度模式會造成大量資源浪費,Spark On Mesos還提供了另外一種排程模式:細粒度模式,這種模式類似於現在的雲端計算,思想是按需分配。與粗粒度模式一樣,應用程式啟動時,先會啟動executor,但每個executor佔用資源僅僅是自己執行所需的資源,不需要考慮將來要執行的任務,之後,mesos會為每個executor動態分配資源,每分配一些,便可以執行一個新任務,單個Task執行完之後可以馬上釋放對應的資源。每個Task會彙報狀態給Mesos slave和Mesos Master,便於更加細粒度管理和容錯,這種排程模式類似於MapReduce排程模式,每個Task完全獨立,優點是便於資源控制和隔離,但缺點也很明顯,短作業執行延遲大。

Spark On YARN模式。這是一種最有前景的部署模式。Spark on YARN模式根據Driver在叢集中的位置分為兩種模式:一種是YARN-Client模式,另一種是YARN-Cluster(或稱為YARN-Standalone模式)。

Yarn-Client模式:Driver在客戶端本地執行,這種模式可以使得Spark Application和客戶端進行互動,因為Driver在客戶端,所以可以通過webUI訪問Driver的狀態,預設是http://hostname:4040訪問,而YARN通過http:// hostname:8088訪問。YARN-client的工作流程步驟為:


1)Spark Yarn Client向YARN的ResourceManager申請啟動Application Master。同時在SparkContent初始化中將建立DAGScheduler和TASKScheduler等,由於我們選擇的是Yarn-Client模式,程式會選擇YarnClientClusterScheduler和YarnClientSchedulerBackend;

2)ResourceManager收到請求後,在叢集中選擇一個NodeManager,為該應用程式分配第一個Container,要求它在這個Container中啟動應用程式的ApplicationMaster,與YARN-Cluster區別的是在該ApplicationMaster不執行SparkContext,只與SparkContext進行聯絡進行資源的分配;

3)Client中的SparkContext初始化完畢後,與ApplicationMaster建立通訊,向ResourceManager註冊,根據任務資訊向ResourceManager申請資源(Container);

4)一旦ApplicationMaster申請到資源(也就是Container)後,便與對應的NodeManager通訊,要求它在獲得的Container中啟動CoarseGrainedExecutorBackend,CoarseGrainedExecutorBackend啟動後會向Client中的SparkContext註冊並申請Task;

5)client中的SparkContext分配Task給CoarseGrainedExecutorBackend執行,CoarseGrainedExecutorBackend執行Task並向Driver彙報執行的狀態和進度,以讓Client隨時掌握各個任務的執行狀態,從而可以在任務失敗時重新啟動任務;

6)應用程式執行完成後,Client的SparkContext向ResourceManager申請登出並關閉自己。

YARN-Cluster模式:當使用者向YARN中提交一個應用程式後,YARN將分兩個階段執行該應用程式:

第一個階段是把Spark的Driver作為一個ApplicationMaster在YARN叢集中先啟動;

第二個階段是由ApplicationMaster建立應用程式,然後為它向ResourceManager申請資源,並啟動Executor來執行Task,同時監控它的整個執行過程,直到執行完成。


1)Spark Yarn Client向YARN中提交應用程式,包括ApplicationMaster程式、啟動ApplicationMaster的命令、需要在Executor中執行的程式等;

2)ResourceManager收到請求後,在叢集中選擇一個NodeManager,為該應用程式分配第一個Container,要求它在這個Container中啟動應用程式的ApplicationMaster,其中ApplicationMaster進行SparkContext等的初始化;

3)ApplicationMaster向ResourceManager註冊,這樣使用者可以直接通過ResourceManage檢視應用程式的執行狀態,然後它將採用輪詢的方式通過RPC協議為各個任務申請資源,並監控它們的執行狀態直到執行結束;

4)一旦ApplicationMaster申請到資源(也就是Container)後,便與對應的NodeManager通訊,要求它在獲得的Container中啟動CoarseGrainedExecutorBackend,CoarseGrainedExecutorBackend啟動後會向ApplicationMaster中的SparkContext註冊並申請Task。這一點和Standalone模式一樣,只不過SparkContext在Spark Application中初始化時,使用CoarseGrainedSchedulerBackend配合YarnClusterScheduler進行任務的排程,其中YarnClusterScheduler只是對TaskSchedulerImpl的一個簡單包裝,增加了對Executor的等待邏輯等;

5)ApplicationMaster中的SparkContext分配Task給CoarseGrainedExecutorBackend執行,CoarseGrained Executor Backend執行Task並向ApplicationMaster彙報執行的狀態和進度,以讓ApplicationMaster隨時掌握各個任務的執行狀態,從而可以在任務失敗時重新啟動任務;

6)應用程式執行完成後,ApplicationMaster向ResourceManager申請登出並關閉自己。

Spark Client 和 SparkCluster的區別:

理解YARN-Client和YARN-Cluster深層次的區別之前先清楚一個概念:Application Master。在YARN中,每個Application例項都有一個ApplicationMaster程式,它是Application啟動的第一個容器。它負責和ResourceManager打交道並請求資源,獲取資源之後告訴NodeManager為其啟動Container。從深層次的含義講YARN-Cluster和YARN-Client模式的區別其實就是ApplicationMaster程式的區別。

YARN-Cluster模式下,Driver執行在AM(Application Master)中,它負責向YARN申請資源,並監督作業的執行狀況。當使用者提交了作業之後,就可以關掉Client,作業會繼續在YARN上執行,因而YARN-Cluster模式不適合執行互動型別的作業。

YARN-Client模式下,ApplicationMaster僅僅向YARN請求Executor,Client會和請求的Container通訊來排程他們工作,也就是說Client不能離開。

1.local模式

local模式主要用於程式的除錯,--master用來指定執行模式,採用本地模式即local,2代表執行程式的執行緒個數。

[root@masteractive src]# spark-shell --master local[2] #執行後將會開啟scala的命令列介面,可訪問spark叢集


[root@masteractive src]#./bin/run-example SparkPi 10 --master local[2] #執行一個spark例子,計算pi的值

2.Standalone模式

[root@master conf]# cp spark-env.sh.template spark-env.sh

修改內容如下。

SPARK_MASTER_HOST=masteractive #master的節點名稱
SPARK_WORKER_CORES=1  #配置每個worker core數量
SPARK_WORKER_MEMORY=1G #配置work記憶體大小
SPARK_WORKER_INSTANCES=1 #配置節點worker的個數
[root@master conf]# cp slaves.template slaves #在slave中寫上從節點的hostname,和hadoop叢集類似。

將修改的配置檔案傳送給子節點,之後啟動叢集。

[root@master sbin]# ./start-all.sh 

啟動後,主節點有個master程式,從節點有個worker程式,通過http://192.168.101.101:8080/檢視叢集資訊。


[root@master spark-2.2.0-bin-2.6.0-cdh5.7.0]# spark-shell --master spark://masteractive:7077 現在通過7077埠對叢集進行訪問

此外可以配置Job History Server 。

spark-default.conf.template 複製為 spark-default.conf,並新增如下內容。

spark.eventLog.enabled  true
spark.eventLog.dir  hdfs://masteractive:9000/sparkeventlog
spark.eventLog.compress true

在spark-env.sh中,新增如下內容。

export SPARK_HISTORY_OPTS="-Dspark.history.ui.port=4000 -Dspark.history.retainedApplications=3 -Dspark.history.fs.logDirectory=hdfs://masteractive:9000/sparkeventlog"

相關引數描述:

spark.eventLog.dirApplication 在執行過程中所有的資訊均記錄在該屬性指定的路徑下。

spark.history.ui.port=4000 調整 WEBUI 訪問日誌的埠號為 4000。

spark.history.fs.logDirectory=hdfs://masteractive:9000/sparkeventlog。配置 了該屬性後,在 start-history-server.sh 時就無需再顯式的指定路徑, Spark History Server 頁面只展示該指定路徑下的日誌資訊。

spark.history.retainedApplications=3 指定儲存 Application 歷史記錄的個數,如果超過這個值,舊的應用程式資訊將被刪除。

現在用standalone叢集來運算pi。 

[root@masteractive spark-2.1.1-bin-hadoop2.6]# ./bin/spark-submit --class org.apache.spark.examples.SparkPi --master spark://masteractive:7077 examples/jars/spark-examples_2.11-2.1.1.jar 100 #迭代100次,運算結果更加準確

3.sparkHA

叢集部署完了,但Master 節點存在單點故障,要解決此問題,就要藉助 zookeeper,並且啟動至少兩個 Master 節點來實現高可靠,配置方式比較簡單。


停止 spark 所有服務,修改配置檔案 spark-env.sh,在該配置檔案中刪掉SPARK_MASTER_IP 並新增如下配置。

export SPARK_DAEMON_JAVA_OPTS="-Dspark.deploy.recoveryMode=ZOOKEEPER -Dspark.deploy.zookeeper.url=masteractive:2181,slave1:2181 -Dspark.deploy.zookeeper.dir=/spark" #將masteractive和slave1設定成master節點。

分別在masteractive和slave1中啟動master節點。

 sbin/start-master.sh

程式中 spark 叢集的訪問地址需要改成:

spark-shell --master spark://masteractive:7077,slave1:7077 

4.yarn模式

修改 Hadoop 配置下的 yarn-site.xml:

<configuration>
<!-- Site specific YARN configuration properties -->
    <property>
        <name>yarn.resourcemanager.hostname</name>
        <value>masteractive</value>
    </property>
    <property>
        <name>yarn.nodemanager.aux-services</name>
        <value>mapreduce_shuffle</value>
    </property>
<!--是否啟動一個執行緒檢查每個任務正使用的實體記憶體量,如果任務超出分配值,則直接將其殺掉,預設是 true -->
    <property>
        <name>yarn.nodemanager.pmem-check-enabled</name>
        <value>false</value>
    </property>
<!--是否啟動一個執行緒檢查每個任務正使用的虛擬記憶體量,如果任務超出分配值,則直接將其殺掉,預設是 true -->
    <property>
        <name>yarn.nodemanager.vmem-check-enabled</name>
        <value>false</value>
    </property>
</configuration>

修改 spark-env.sh 新增:

HADOOP_CONF_DIR=/usr/local/src/hadoop-2.6.0-cdh5.7.0/etc/hadoop
YARN_CONF_DIR=/usr/local/src/hadoop-2.6.0-cdh5.7.0/etc/hadoop

啟動yarn任務,計算pi。

./bin/spark-submit --class org.apache.spark.examples.SparkPi --master yarn-cluster examples/jars/spark-examples_2.11-2.1.1.jar 10

五、執行spark程式

在上面配置不同模式的過程中,執行了計算pi的spark程式。下面對這些引數進行具體講解。

1.提交應用的指令碼

一旦打包好就可以使用 bin/spark-submit 指令碼啟動應用了這個指令碼負責設定 spark 使用的 classpath 和依賴支援不同型別的叢集管理器和釋出模式:

./bin/spark-submit \
--class <main-class>
--master <master-url> \
--deploy-mode <deploy-mode> \
--conf <key>=<value> \
... # other options
<application-jar> \
[application-arguments]

一些常用選項:

 ① --class: 你的應用的啟動類 (如 org.apache.spark.examples.SparkPi)

 ② --master: 叢集的 master URL (如 spark://23.195.26.187:7077)

 ③ --deploy-mode: 是否釋出你的驅動到 worker 節點(cluster) 或者作為一個本地客戶端 (client) (default:client)

 ④ --conf: 任意的 Spark 配置屬性, 格式 key=value。如果值包含空格,可以加引號“key=value”。

 ⑤ application-jar: 打包好的應用 jar,包含依賴。

 ⑥ application-arguments: 傳給 main()方法的引數。

Master URL 可以是以下格式: 

local本地以一個 worker 執行緒執行(例如非並行的情況).
local[K]本地以 K worker 執行緒 (理想情況下, K 設定為你機器
的 
CPU 核數).
local[*]本地以本機同樣核數的執行緒執行.
spark://HOST:PORT連線到指定的 Spark standalone cluster master. 埠是
你的 
master 叢集配置的埠,預設值為 7077.
mesos://HOST:PORT連線到指定的 Mesos 叢集. Port 是你配置的 mesos 
口, 預設是 
5050. 或者如果 Mesos 使用 ZOoKeeper,
格式為 mesos://zk://....
yarn-client以 client 模式連線到 YARN cluster. 叢集的位置基於
HADOOP_CONF_DIR 變數找到.
yarn-cluster以 cluster 模式連線到 YARN cluster. 叢集的位置基於
HADOOP_CONF_DIR 變數找到.

2.執行spark 程式(standalone

spark-submit \
--class org.apache.spark.examples.SparkPi \
--master spark://masteractive:7077 \
--executor-memory 1G \
--total-executor-cores 2 \
/usr/local/src/spark-2.1.1-bin-hadoop2.6/examples/jars/spark-examples_2.11-2.1.1.jar 

--master spark://masteractive:7077    指定 Master 的地址

--executor-memory 1G                      指定每個 executor 可用記憶體為 1G

--total-executor-cores 2                     指定每個 executor 使用的 cup 核數為 2

該演算法是利用蒙特·卡羅演算法求 PI,檢視日誌頁面如下。


3.執行spark 程式(yarn

spark-submit \
--class org.apache.spark.examples.SparkPi \
--master spark://masteractive:7077 \
--deploy-mode client \
/usr/local/src/spark-2.1.1-bin-hadoop2.6/examples/jars/spark-examples_2.11-2.1.1.jar #這個也可是自己編寫的程式碼jar包

4.Wordcount案例(standalone)

先用standalone的模式啟動spark-shell。(參見上文)

scala> var file = spark.sparkContext.textFile("/usr/local/src/data/1.txt") #讀取檔案
scala> val wordCounts = file.flatMap(line =>line.split(" ")).map((word =>(word,1))).reduceByKey(_+_) #計算邏輯
scala> wordCounts.collect #計算,並輸出結果
res0: Array[(String, Int)] = Array((dajifd,1), (sdajif,1), (aijdjaiZZ,1), (sajdfi,1), (i,1), (ba,1))

上面講解了spark的編譯安裝、幾種模式和提交程式的方法,如果能正常出結果,那麼說明這個spark的叢集可以使用了。下面具體開始介紹spark內部的實現機制,瞭解spark是如何工作的。

六、Sparkcore

1.RDD概念

RDD Spark 的基石,是實現 Spark 資料處理的核心抽象。Hadoop MapReduce 是一種基於資料集的工作模式,面向資料,這種工作模式一般是從儲存上載入資料集,然後運算元據集,最後寫入物理儲存裝置。資料更多面臨的是一次性處理。MR 的這種方式對資料領域兩種常見的操作不是很高效。第一種是迭代式的演算法。比如機器學習中 ALS、凸優化梯度下降等。這些都需要基於資料集或者資料集的衍生資料反覆查詢反覆操作。 MR 這種模式不太合適,即使多 MR序列處理,效能和時間也是一個問題。資料的共享依賴於磁碟。另外一種是互動式資料探勘, MR 顯然不擅長。


我們需要一個效率非常快, 且能夠支援迭代計算和有效資料共享的模型,Spark 應運而生。 RDD 是基於資料集的工作模式, 更多的是面向工作流。


RDD(Resilient Distributed Dataset)叫做彈性分散式資料集,是 Spark 中最基本的資料抽象,它代表一個不可變、可分割槽、裡面的元素可平行計算的集合,可以簡單的理解為在磁碟上有個資料集,這個資料集載入到spark程式中就變成了RDD的概念,接下來所說的RDD,就可理解為載入到記憶體中的資料集。在Spark中,對資料的所有操作不外乎建立RDD、轉化已有RDD以及呼叫RDD操作進行求值。 每個RDD都可被分為多個分割槽,這些分割槽執行在叢集中的不同節點上。RDD可以包含Python、Java、Scala中任意型別的物件, 甚至可以包含使用者自定義的物件。RDD具有資料流模型的特點:自動容錯、位置感知性排程和可伸縮性。RDD允許使用者在執行多個查詢時顯式地將資料集快取在記憶體中,後續的查詢能夠重用資料集,這極大地提升了查詢速度。

RDD支援兩種操作:轉化操作(transformation)和行動操作(action)。RDD的轉化操作是返回一個新的RDD的操作,比如 map()和 filter(),而行動操作則是向驅動器程式返回結果或把結果寫入外部系統的操作, 比如count()和 first()。Spark 採用惰性計算模式,RDD只有第一次在一個行動操作中用到時,才會真正計算。Spark可以優化整個計算過程。 預設情況下,Spark的 RDD會在你每次對它們進行行動操作時重新計算。如果想在多個行動操作中重用同一RDD,可以使用 RDD.persist() Spark 把這個 RDD 快取下來。


2.RDD 屬性

1)一組分片(Partition),即資料集的基本組成單位。對於 RDD 來說,每個分片都會被一個計算任務處理,並決定平行計算的粒度。使用者可以在建立RDD時指定RDD的分片個數,如果沒有指定,那麼就會採用預設值。預設值就是程式所分配到的CPU Core的數目。

2一個計算每個分割槽的函式。SparkRDD的計算是以分片為單位的,每個RDD都會實現compute函式以達到這個目的。 compute函式會對迭代器進行復合,不需要儲存每次計算的結果。

3RDD之間的依賴關係。RDD的每次轉換都會生成一個新的RDD,所以RDD之間就會形成類似於流水線一樣的前後依賴關係。在部分分割槽資料丟失時,Spark 可以通過這個依賴關係重新計算丟失的分割槽資料,而不是對 RDD 的所有分割槽進行重新計算。

4 一個Partitioner,即RDD的分片函式。當前 Spark 中實現了兩種型別的分片函式 ,一 個是基於雜湊的HashPartitioner , 另外一個是基於範圍的RangePartitioner。只有對於有key-valueRDD,才會有Partitioner,非keyvalueRDDParititioner 的值是NonePartitioner函式不但決定了RDD本身的分片數量,也決定了parent RDD Shuffle輸出時的分片數量。

5一個列表,儲存存取每個Partition的優先位置(preferred location)。對於一 HDFS 檔案來說,這個列表儲存的就是每個 Partition所在的塊的位置。按照“移動資料不如移動計算”的理念, Spark 在進行任務排程的時候,會盡可能地將計算任務分配到其所要處理資料塊的儲存位置。

RDD是一個應用層面的邏輯概念。 一個RDD多個分片。 RDD就是一個後設資料記錄集,記錄了 RDD 記憶體所有的關係資料。

3.RDD特點

RDD 表示只讀的分割槽的資料集,對 RDD 進行改動,只能通過 RDD 的轉換操作,由一個 RDD 得到一個新的 RDD,新的 RDD 包含了從其他 RDD 衍生所必需的資訊。 RDDs 之間存在依賴, RDD 的執行是按照血緣關係延時計算的。如果血緣關係較長,可以通過持久化 RDD 來切斷血緣關係。

3.1分割槽

RDD 邏輯上是分割槽的,每個分割槽的資料是抽象存在的,計算的時候會通過一個 compute 函式得到每個分割槽的資料。如果 RDD 是通過已有的檔案系統構建,則 compute 函式是讀取指定檔案系統中的資料,如果 RDD 是通過其他RDD 轉換而來,則 compute 函式是執行轉換邏輯將其他 RDD 的資料進行轉換。

RDD 是隻讀的,要想改變 RDD 中的資料,只能在現有的RDD 基礎上建立新的 RDD由一個 RDD 轉換到另一個 RDD,可以通過豐富的操作運算元實現 。

3.2依賴

RDDs 通過操作運算元進行轉換,轉換得到的新 RDD 包含了從其他 RDDs衍生所必需的資訊, RDDs 之間維護著這種血緣關係,也稱之為依賴。如下圖所示,依賴包括兩種,一種是窄依賴, RDDs 之間分割槽是一一對應的,另一種是寬依賴,下游 RDD 的每個分割槽與上游 RDD(也稱之為父 RDD)的每個分割槽都有關,是多對多的關係。 

通過 RDDs 之間的這種依賴關係,一個任務流可以描述為 DAG(有向無環圖),如下圖所示,在實際執行過程中寬依賴對應於 Shuffle(圖中的reduceByKey join),窄依賴中的所有轉換操作可以通過類似於管道的方式一氣呵成執行(圖中 map union 可以一起執行)


3.3快取

如果在應用程式中多次使用同一個 RDD,可以將該 RDD 快取起來,該RDD 只有在第一次計算的時候會根據血緣關係得到分割槽的資料,在後續其他地方用到該 RDD 的時候,會直接從快取處取而不用再根據血緣關係計算,這樣就加速後期的重用。如下圖所示, RDD-1 經過一系列的轉換後得到 RDD-n並儲存到 hdfsRDD-1 在這一過程中會有個中間結果,如果將其快取到內存,那麼在隨後的 RDD-1 轉換到 RDD-m 這一過程中,就不會計算其之前的RDD-0 了。 


3.4checkpoint

雖然 RDD 的血緣關係天然地可以實現容錯,當 RDD 的某個分割槽資料失敗或丟失,可以通過血緣關係重建。但是對於長時間迭代型應用來說,隨著迭代的進行, RDDs 之間的血緣關係會越來越長,一旦在後續迭代過程中出錯,則需要通過非常長的血緣關係去重建,勢必影響效能。為此, RDD 支援checkpoint 將資料儲存到持久化的儲存中,這樣就可以切斷之前的血緣關係,因為 checkpoint 後的 RDD 不需要知道它的父 RDDs 了,它可以從 checkpoint處拿到資料。

給定一個 RDD 我們至少可以知道如下幾點資訊: 1、分割槽數以及分割槽方式; 2、由父 RDDs 衍生而來的相關依賴資訊; 3、計算每個分割槽的資料,計算步驟為: 1)如果被快取,則從快取中取的分割槽的資料; 2)如果被checkpoint,則從 checkpoint 處恢復資料; 3)根據血緣關係計算分割槽的資料。 

4.RDD程式設計

這裡主要介紹常用函式,具體使用自行找資料學習。

map(func) 返回一個新的 RDD,該 RDD 由每一個輸入元素經過 func 函式轉換後組成
filter(func) 返回一個新的 RDD,該 RDD 由經過 func函式計算後返回值為 true 的輸入元素組成
flatMap(func) 類似於 map,但是每一個輸入元素可以被對映為 0 或多個輸出元素(所以 func 應該返回一個序列,而不是單一元素)
mapPartitions(func) 類似於 map,但獨立地在 RDD 的每一個分片上執行,因此在型別為 T RDD 上執行時, func 的函式型別必須是Iterator[T] => Iterator[U]。 假設有 N 個元素,有 M 個分割槽,那麼 map 的函式的將被呼叫 N 次,而 mapPartitions 被呼叫 M 次,一個函式一次處理所有分割槽
mapPartitionsWithIndex(func) 類似於 mapPartitions,但 func 帶有一個整數參數列示分片的索引值,因此在型別為T RDD 上執行時, func 的函式型別必須是(Int, Interator[T])  => Iterator[U]
sample(withReplacement,fraction, seed)以指定的隨機種子隨機抽樣出數量為fraction 的資料,ithReplacement 表示是抽出的資料是否放回, true 為有放回的抽樣, false 為無放回的抽樣, seed 用於指定隨機數生成器種子。例子從 RDD 中隨機且有放回的抽出 50%的資料,隨機種子值為 3(即可能以 1 2 3 的其中一個起始值)
union(otherDataset) 對源 RDD 和引數 RDD 求並集後返回一個新的 RDD
intersection(otherDataset) 對源 RDD 和引數 RDD 求交集後返回一個新的 RDD
distinct([numTasks])) 對源 RDD 進行去重後返回一個新的 RDD.預設情況下,只有 8 個並行任務來操作,但是可以傳入一個可選的 numTasks 引數改變它。
partitionBy RDD 進行分割槽操作,如果原有的partionRDD 和現有的 partionRDD 是一致的話就不進行分割槽,否則會生成 ShuffleRDD.
reduceByKey(func,[numTasks])在一個(K,V)RDD 上呼叫,返回一個(K,V)RDD,使用指定的 reduce 函式,將相同 key 的值聚合到一起, reduce 任務的個數可以通過第二個可選的引數來設定
groupByKey groupByKey 也是對每個 key 進行操作,但只生成一個 sequence
combineByKey[C](
createCombiner: V => C,
mergeValue: (C, V) => C,
mergeCombiners: (C, C) => C)
對相同 K,把 V 合併成一個集合.
createCombiner: combineByKey() 會遍歷分割槽中的所有元素,因此每個元素的鍵要麼還沒有遇到過,要麼就 和之前的某個元素的鍵相同。
如果這是一個新的元素
,combineByKey() 會使用一個叫作createCombiner() 的函式來建立那個鍵對應的累加器的初始值
mergeValue: 如果這是一個在處理當前分割槽之前已經遇到的鍵, 它會使用 mergeValue() 方法將該鍵的累加器對應的當前值與這個新的值進
行合併
mergeCombiners: 由於每個分割槽都是獨立處理的, 因此對於同一個鍵可以有多個累加器。如果有兩個或者更多的分割槽都有對應同一個鍵的
累加器, 就需要使用使用者提供的
mergeCombiners() 方法將各個分割槽的結果進行合併。
aggregateByKey(zeroValue:U,[partitioner: Partitioner]) (seqOp:
(U, V) => U,combOp: (U, U) =>U)
kv 對的 RDD 中,,按 key value 進行分組合並,合併時,將每個 value 和初始值作為 seq 函式的引數,進行計算,返回的結果作為一個新的 kv 對,然後再將結果按照 key 進行合併,最後將每個分組
value 傳遞給 combine 函式進行計算(先將前兩個 value 進行計算,將返回結果和下一個 value 傳給 combine 函式,以此類推),將 key 與計算結果作為一個新的 kv 對輸出。seqOp 函式用於在每一個分割槽中用初始值逐步迭代 valuecombOp 函式用於合併每個分割槽中的結果




















相關文章