Spark技術手冊

一 總覽

1.1 spark說明:

官方原話:Lightning-fast cluster computing【快如閃電的批處理框架】

快速、通用的大數據處理引擎。

1.2 spark特點:

1)提供 Cache 機制來支持需要反覆迭代計算或者多次數據共享,減少數據讀取的 IO 開銷;

2)提供了一套支持 DAG 圖的分佈式並行計算的編程框架,減少多次計算之間中間結果寫到 Hdfs 的開銷;

3)使用多線程池模型減少 Task 啓動開稍, shuffle 過程中避免不必要的 sort 操作並減少磁盤 IO 操作。(Hadoop 的 Map 和 reduce 之間的 shuffle 需要 sort)

1.3 spark框架:

  • Spark SQL: 提供了類 SQL 的查詢,返回 Spark-DataFrame 的數據結構
  • Spark Streaming: 流式計算,主要用於處理線上實時時序數據
  • MLlib: 提供機器學習的各種模型和調優
  • GraphX: 提供基於圖的算法,如 PageRank

1.4 spark工作原理:

 

  • 應用程序(Application): 基於Spark的用戶程序,包含了一個Driver Program 和集羣中多個的Executor;
  • 驅動(Driver): 運行Application的main()函數並且創建SparkContext;
  • 執行單元(Executor): 是爲某Application運行在Worker Node上的一個進程,該進程負責運行Task,並且負責將數據存在內存或者磁盤上,每個Application都有各自獨立的Executors;
  • 集羣管理程序(Cluster Manager): 在集羣上獲取資源的外部服務(例如:Local、Standalone、Mesos或Yarn等集羣管理系統);
  • 操作(Operation): 作用於RDD的各種操作分爲Transformation和Action.

 

整個 Spark 集羣中,分爲 Master 節點與 worker 節點,,其中 Master 節點上常駐 Master 守護進程和 Driver 進程, Master 負責將串行任務變成可並行執行的任務集Tasks, 同時還負責出錯問題處理等,而 Worker 節點上常駐 Worker 守護進程, Master 節點與 Worker 節點分工不同, Master 負載管理全部的 Worker 節點,而 Worker 節點負責執行任務. 

  Driver 的功能是創建 SparkContext, 負責執行用戶寫的 Application 的 main 函數進程,Application 就是用戶寫的程序. 

Spark 支持不同的運行模式,包括Local, Standalone,Mesoses,Yarn 模式.不同的模式可能會將 Driver 調度到不同的節點上執行.集羣管理模式裏, local 一般用於本地調試. 

  每個 Worker 上存在一個或多個 Executor 進程,該對象擁有一個線程池,每個線程負責一個 Task 任務的執行.根據 Executor 上 CPU-core 的數量,其每個時間可以並行多個 跟 core 一樣數量的 Task4.Task 任務即爲具體執行的 Spark 程序的任務. 

 

“我們使用spark-submit提交一個Spark作業之後,這個作業就會啓動一個對應的Driver進程。根據你使用的部署模式(deploy-mode)不同,Driver進程可能在本地啓動,也可能在集羣中某個工作節點上啓動。而Driver進程要做的第一件事情,就是向集羣管理器(可以是Spark Standalone集羣,也可以是其他的資源管理集羣,美團•大衆點評使用的是YARN作爲資源管理集羣)申請運行Spark作業需要使用的資源,這裏的資源指的就是Executor進程。YARN集羣管理器會根據我們爲Spark作業設置的資源參數,在各個工作節點上,啓動一定數量的Executor進程,每個Executor進程都佔有一定數量的內存和CPU core。 

  在申請到了作業執行所需的資源之後,Driver進程就會開始調度和執行我們編寫的作業代碼了。Driver進程會將我們編寫的Spark作業代碼分拆爲多個stage,每個stage執行一部分代碼片段,併爲每個stage創建一批Task,然後將這些Task分配到各個Executor進程中執行。Task是最小的計算單元,負責執行一模一樣的計算邏輯(也就是我們自己編寫的某個代碼片段),只是每個Task處理的數據不同而已。一個stage的所有Task都執行完畢之後,會在各個節點本地的磁盤文件中寫入計算中間結果,然後Driver就會調度運行下一個stage。下一個stage的Task的輸入數據就是上一個stage輸出的中間結果。如此循環往復,直到將我們自己編寫的代碼邏輯全部執行完,並且計算完所有的數據,得到我們想要的結果爲止。 

  Spark是根據shuffle類算子來進行stage的劃分。如果我們的代碼中執行了某個shuffle類算子(比如reduceByKey、join等),那麼就會在該算子處,劃分出一個stage界限來。可以大致理解爲,shuffle算子執行之前的代碼會被劃分爲一個stage,shuffle算子執行以及之後的代碼會被劃分爲下一個stage。因此一個stage剛開始執行的時候,它的每個Task可能都會從上一個stage的Task所在的節點,去通過網絡傳輸拉取需要自己處理的所有key,然後對拉取到的所有相同的key使用我們自己編寫的算子函數執行聚合操作(比如reduceByKey()算子接收的函數)。這個過程就是shuffle。 

  當我們在代碼中執行了cache/persist等持久化操作時,根據我們選擇的持久化級別的不同,每個Task計算出來的數據也會保存到Executor進程的內存或者所在節點的磁盤文件中。 

  因此Executor的內存主要分爲三塊:第一塊是讓Task執行我們自己編寫的代碼時使用,默認是佔Executor總內存的20%;第二塊是讓Task通過shuffle過程拉取了上一個stage的Task的輸出後,進行聚合等操作時使用,默認也是佔Executor總內存的20%;第三塊是讓RDD持久化時使用,默認佔Executor總內存的60%。 

  Task的執行速度是跟每個Executor進程的CPU core數量有直接關係的。一個CPU core同一時間只能執行一個線程。而每個Executor進程上分配到的多個Task,都是以每個Task一條線程的方式,多線程併發運行的。如果CPU core數量比較充足,而且分配到的Task數量比較合理,那麼通常來說,可以比較快速和高效地執行完這些Task線程。 ”

發佈方式:

 

 

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註銷,釋放資源

 

  • Apache Mesos
  • Hadoop Yarn

Spark on YARN模式根據Driver在集羣中的位置分爲兩種模式:一種是YARN-Client模式,另一種是YARN-Cluster(或稱爲YARN-Standalone模式)

Yarn-Client模式中,Driver在客戶端本地運行,這種模式可以使得Spark Application和客戶端進行交互,因爲Driver在客戶端,所以可以通過webUI訪問Driver的狀態,默認是http://hadoop1:4040訪問,而YARN通過http:// hadoop1:8088訪問

YARN-client的工作流程步驟爲:

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

ResourceManager收到請求後,在集羣中選擇一個NodeManager,爲該應用程序分配第一個Container,要求它在這個Container中啓動應用程序的ApplicationMaster,與YARN-Cluster區別的是在該ApplicationMaster不運行SparkContext,只與SparkContext進行聯繫進行資源的分派

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

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

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

應用程序運行完成後,Client的SparkContext向ResourceManager申請註銷並關閉自己

 

Spark Cluster模式:

在YARN-Cluster模式中,當用戶向YARN中提交一個應用程序後,YARN將分兩個階段運行該應用程序:

第一個階段是把Spark的Driver作爲一個ApplicationMaster在YARN集羣中先啓動;

第二個階段是由ApplicationMaster創建應用程序,然後爲它向ResourceManager申請資源,並啓動Executor來運行Task,同時監控它的整個運行過程,直到運行完成

YARN-cluster的工作流程分爲以下幾個步驟

Spark Yarn Client向YARN中提交應用程序,包括ApplicationMaster程序、啓動ApplicationMaster的命令、需要在Executor中運行的程序等

ResourceManager收到請求後,在集羣中選擇一個NodeManager,爲該應用程序分配第一個Container,要求它在這個Container中啓動應用程序的ApplicationMaster,其中ApplicationMaster進行SparkContext等的初始化

ApplicationMaster向ResourceManager註冊,這樣用戶可以直接通過ResourceManage查看應用程序的運行狀態,然後它將採用輪詢的方式通過RPC協議爲各個任務申請資源,並監控它們的運行狀態直到運行結束

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

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

應用程序運行完成後,ApplicationMaster向ResourceManager申請註銷並關閉自己

  • Kubernetes

二 RDD編程指南

2.1 引入spark jar包

maven配置:

groupId = org.apache.spark 

artifactId = spark-core_2.11 

version = 2.2.1

文件引入:

import org.apache.spark.api.java.JavaSparkContext;

import org.apache.spark.api.java.JavaRDD;

import org.apache.spark.SparkConf;

2.2 RDDS(resilient distributed dataset)彈性分佈式數據集

一個能容錯的元素收集器,支持並行處理

創建RDDs的兩種方式:

parallelizing an existing collection in your driver program

referencing a dataset in an external storage system,such as a shared filesystem, HDFS, HBase, or any data source offering a Hadoop InputFormat.

舉例:

第一種方式:

List<Integer> data = Arrays.asList(1, 2, 3, 4, 5); JavaRDD<Integer> distData = sc.parallelize(data);

或者:

JavaRDD<Integer> distData = sc.parallelize(data,10);

PS:第二個參數爲手動設置分區數

第二種方式:

JavaRDD<String> distFile = sc.textFile("data.txt");

PS:

Some notes on reading files with Spark:

  • If using a path on the local filesystem, the file must also be accessible at the same path on worker nodes. Either copy the file to all workers or use a network-mounted shared file system.
  • All of Spark’s file-based input methods, including textFile, support running on directories, compressed files, and wildcards as well. For example, you can use textFile("/my/directory"), textFile("/my/directory/*.txt"), and textFile("/my/directory/*.gz").
  • The textFile method also takes an optional second argument for controlling the number of partitions of the file. By default, Spark creates one partition for each block of the file (blocks being 128MB by default in HDFS), but you can also ask for a higher number of partitions by passing a larger value. Note that you cannot have fewer partitions than blocks.

Apart from text files, Spark’s Java API also supports several other data formats:

  • JavaSparkContext.wholeTextFiles lets you read a directory containing multiple small text files, and returns each of them as (filename, content) pairs. This is in contrast with textFile, which would return one record per line in each file.
  • For SequenceFiles, use SparkContext’s sequenceFile[K, V] method where K and V are the types of key and values in the file. These should be subclasses of Hadoop’s Writable interface, like IntWritable and Text.
  • For other Hadoop InputFormats, you can use the JavaSparkContext.hadoopRDD method, which takes an arbitrary JobConf and input format class, key class and value class. Set these the same way you would for a Hadoop job with your input source. You can also use JavaSparkContext.newAPIHadoopRDD for InputFormats based on the “new” MapReduce API (org.apache.hadoop.mapreduce).
  • JavaRDD.saveAsObjectFile and JavaSparkContext.objectFile support saving an RDD in a simple format consisting of serialized Java objects. While this is not as efficient as specialized formats like Avro, it offers an easy way to save any RDD.

RDD的基本操作:

 RDD 的操作函數(operation)主要分爲2種類型 Transformation 和 Action.

類別

函數

區別

Transformation

Map,filter,groupBy,join, union,reduce,sort,partitionBy

返回值還是 RDD,不會馬上 提交 Spark 集羣運行

Action

count,collect,take,save, show

返回值不是 RDD,會形成 DAG 圖,提交 Spark 集羣運行 並立即返回結果

Transformation 操作不是馬上提交 Spark 集羣執行的,Spark 在遇到 Transformation 操作時只會記錄需要這樣的操作,並不會去執行,需要等到有 Action 操作的時候纔會真正啓動計算過程進行計算.針對每個 Action,Spark 會生成一個 Job, 從數據的創建開始,經過 Transformation, 結尾是 Action 操作.這些操作對應形成一個有向無環圖(DAG),形成 DAG 的先決條件是最後的函數操作是一個Action. 

有了計算的DAG圖,Spark內核下一步的任務就是根據DAG圖將計算劃分成任務集,也就是Stage,這樣可以將任務提交到計算節點進行真正的計算。Spark計算的中間結果默認是保存在內存中的,Spark在劃分Stage的時候會充分考慮在分佈式計算中可流水線計算(pipeline)的部分來提高計算的效率,而在這個過程中,主要的根據就是RDD的依賴類型。根據不同的transformation操作,RDD的依賴可以分爲窄依賴(Narrow Dependency)和寬依賴(Wide Dependency,在代碼中爲ShuffleDependency)兩種類型。窄依賴指的是生成的RDD中每個partition只依賴於父RDD(s) 固定的partition。寬依賴指的是生成的RDD的每一個partition都依賴於父 RDD(s) 所有partition。窄依賴典型的操作有map, filter, union等,寬依賴典型的操作有groupByKey, sortByKey等。可以看到,寬依賴往往意味着shuffle操作,這也是Spark劃分stage的主要邊界。對於窄依賴,Spark會將其儘量劃分在同一個stage中,因爲它們可以進行流水線計算。

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

 

RDD運行流程:

  • RDD在Spark中運行大概分爲以下三步:
    1. 創建RDD對象
    2. DAGScheduler模塊介入運算,計算RDD之間的依賴關係,RDD之間的依賴關係就形成了DAG
    3. 每一個Job被分爲多個Stage。劃分Stage的一個主要依據是當前計算因子的輸入是否是確定的,如果是則將其分在同一個Stage,避免多個Stage之間的消息傳遞開銷
  • 示例圖如下:

  • 以下面一個按 A-Z 首字母分類,查找相同首字母下不同姓名總個數的例子來看一下 RDD 是如何運行起來的
  • 創建 RDD  上面的例子除去最後一個 collect 是個動作,不會創建 RDD 之外,前面四個轉換都會創建出新的 RDD 。因此第一步就是創建好所有 RDD( 內部的五項信息 )?
  • 創建執行計劃 Spark 會儘可能地管道化,並基於是否要重新組織數據來劃分 階段 (stage) ,例如本例中的 groupBy() 轉換就會將整個執行計劃劃分成兩階段執行。最終會產生一個 DAG(directed acyclic graph ,有向無環圖 ) 作爲邏輯執行計劃
  • 調度任務  將各階段劃分成不同的 任務 (task) ,每個任務都是數據和計算的合體。在進行下一階段前,當前階段的所有任務都要執行完成。因爲下一階段的第一個轉換一定是重新組織數據的,所以必須等當前階段所有結果數據都計算出來了才能繼續。

Hadoop小提示:

namenode:是整個文件系統的管理節點。它維護着

1.整個文件系統的文件目錄樹

2.文件/目錄的元信息和每個文件對應的數據塊列表

3.接收用戶的操作請求。

datanode:提供真實文件數據的存儲服務。

nodemanager:是執行在單個節點上的代理,它管理Hadoop集羣中單個計算節點,功能包含與ResourceManager保持通信

 

Spark調優:

1、excutor數

公式:execuoterNum = spark.cores.max/spark.executor.cores

相關參數在啓動具體應用時指定 

例如啓動基於standlone模式的spark sql的thrift 接口時 設置 這兩個參數

--total-executor-cores --executor-cores

它們共同決定了當前應用 啓動executor的個數

 

參考資料:

http://blog.csdn.net/databatman/article/details/53023818

http://developer.51cto.com/art/201502/464742.htm

https://www.cnblogs.com/tgzhu/p/5818374.html

https://www.jianshu.com/p/65a3476757a5

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章