Spark
Spark 作為分散式計算框架,基於 MapReduce 框架開發,但是也有以下區別:
- Spark 基於 Scala 語言開發,MR 基於 Java 語言開發;Scala 是函數語言程式設計語言,對於函式間相互呼叫效率更高;而 Java 是面嚮物件語言,函式間呼叫必須依賴於物件,效率低。
- MapReduce 核心是一次性計算,不適合迭代計算,因為中間結果必須存檔,後續步驟依賴於當前中間結果,會造成大量的磁碟IO,導致效率低;Spark 最佳化了執行邏輯,前一步執行的結果儲存到記憶體而不是磁碟,所以可以提取出重複的步驟,封裝成函式。
- Spark 的 RDD(彈性分散式資料集)支援資料的自動快取,使得重複的資料可以快速訪問,無需重新從磁碟讀取。
- Spark 和 MapReduce 各有利弊,MapReduce 因為依賴於磁碟所以能夠保證程式一定執行完畢,Spark 因為依賴於記憶體,可能會出現記憶體溢位等問題。
- 如果舊專案是基於 Hadoop 的,現在需要遷移到 Spark,那麼就可以使用
On Yarn
模型,基於 Yarn 排程。
Spark 內建模組:
Spark SQL
:提供 HQL 與 Spark 進行互動處理的 API,每個資料表當作一個 RDD,Spark SQL 查詢被轉化為 Spark 操作;Spark Streaming
:對實時資料流進行處理和控制;MLib
:機器學習演算法庫;Graphix
:控制圖、並行圖操作和計算的一組演算法和工具的集合;Spark Core
:底層 RDD API基礎庫,其他模組都會依賴;
RDD
RDD(Resilient Distributed Datasets,彈性分散式資料集)在 Spark 計算過程中扮演重要的角色。Spark 的計算任務始於 SparkContext
上下文物件,它負責資源的申請、任務的排程以及 RDD 的管理和建立。
RDD 特點
RDD 具有以下特點:
- 每個 RDD 都是隻讀的,是分佈在叢集中的只讀物件的集合;
- 一個 RDD 由多個 Partition 分割槽組成,每個分割槽可能分佈在不同節點的記憶體中;
- RDD 之間可以執行轉換操作,轉換但是不計算,每次計算後的結果都需要儲存為新的 RDD;
RDD 操作
RDD 主要包含了以下兩種方法,透過兩種方法不斷對資料處理儲存,最終實現高效、可靠的大資料處理任務:
- 轉換運算元:
- 將 Scala 集合或者 Hadoop 輸入資料構造一個新的 RDD;
- 透過已有的 RDD 產生新的 RDD;
- 惰性執行,只記錄轉換關係不執行計算;
- 常見操作包括
map、filter、flatmap、union、distinct、sortbykey
;
- 動作運算元:
- 透過 RDD 計算得到新的一組值;
- 觸發真正的計算;
- 常見操作包括
first、count、collect、foreach、saveAsTextFile
等;
比如 rdd.map(_+1).saveAsTextFile("hdfs://node01:9000")
操作,map
對應轉換,saveAsTextFile
對應計算。
RDD 依賴
RDD 中區分寬窄依賴,因為寬依賴回覆起來速度很慢。
窄依賴:
- 父 RDD 中分割槽最多被一個子 RDD 分割槽使用;
- 子 RDD 如果部分分割槽資料丟失或損壞,只需要從父 RDD 重新計算恢復;
- 窄依賴可以並行地生成子 RDD 的分割槽,使得任務可以在多個節點上並行執行;
- 常見操作如
map、filter、union、sample
寬依賴:
- 主要發生在需要跨分割槽的資料重組或者聚合操作;
- 子 RDD 分割槽依賴 父RDD 的所有分割槽;
- 子 RDD 如果部分或者全部分割槽資料丟失或損壞,必須從所有父 RDD 分割槽重新計算;
- 寬依賴會導致資料在不同的分割槽間進行洗牌,資料需要重新分配到不同的分割槽,涉及到大量的資料移動和 IO 操作,執行寬依賴時效能可能會受到影響;
- 例如:
groupByKey、reduceByKey、sortByKey、join
;
舉個例子
- 首先透過
SparkContext
上下文物件載入 HDFS 某個目錄下的檔案; - 針對每一行資料按照分表符切分成多個單詞,得到新的單詞集合;
- 對每個單詞執行 map 處理,具體就是標記次數為 1;
- 按照 Key 值執行 Reduce 處理,將 Key 相同的所有 Value 累加求和;(程式碼中的
_
是 Scala 語法中的佔位符) - 將最終結果儲存到 HDFS 下的檔案目錄。
由於每個階段都會儲存新的 RDD,所以如果某階段計算失敗,就可以利用上一個步驟的 RDD 結果重新執行計算,而不需要從源頭開始計算。
Spark 程式
程式架構
Spark 也是主從架構,包含管理節點 Master、工作節點 Worker。
- 構建執行環境,Driver 建立
SparkContext
上下文; - SparkContext 向資源管理器(standalone、Mesos、Yarn)申請
Executor
資源,資源管理器啟動StandaloneExecutorBackend
; - Executor 向
SparkContext
申請 Task; - RDD 物件構建成 DAG 圖,
DAG Scheduler
將 DAG圖解析為 Stage,每個 Stage 對應一個 TaskSet,然後將其傳送給TaskScheduler
,由TaskScheduler
排程傳送給 Executor 執行; - Task 在
Executor
執行完畢後釋放資源;
Spark 作業執行模式
Local 模式:
Spark 應用以多執行緒方式執行在本地,方便除錯。
- local:啟動一個 Executor;
- local[k]:啟動 k 個 Executor;
- local[*]:啟動 CPU 核數個 Executor;
standalone 模式:
分散式叢集執行,不依賴於第三方資源管理系統(Yarn、Mesos等)。
- 採用 Master-Slave 結構;
- Driver 執行於 worker,Master 只負責叢集管理;
- Driver 向 Master 申請作業執行所需資源,Master 分配 Executor 給 Driver;
- Driver 將解析後的 Task 排程到 Executor 上執行,並且不間斷監聽執行情況,彙報給 Master;
- 整體架構類似於 Yarn,其中
Master——ResourceManager
、Driver——Container
、Executor——ApplicationMaster
; - 為了避免 Master 單點故障,由 Zookeeper 負責 Master 節點叢集高可用。
Spark On Yarn:
分散式部署叢集、資源、任務監控由 Yarn 管理,目前僅支援粗粒度資源分配方式。
這種模式的工作流程:
Driver
解析 Spark 資料生成一定量的 Task,然後需要開始申請Hadoop
資源;- 但是 Dirver 無法申請,需要藉助
ApplicationMaster
申請,所以在 Driver 外部套用一個 ApplicationMaster 用作資源申請; - ApplicationMaster 透過向 ResourceManager 申請 Container 資源,到手後通知 Driver;
- Driver 將解析好的作業分發到 Container 節點上執行。
Yarn 包括 Yarn-Client
(適用於互動和除錯)、Yarn-Cluster
(適用於生產環境)兩種模式。
程式執行及底層邏輯
生成邏輯執行計劃
透過 Scala 語言編寫 Spark 邏輯查詢計劃:
sc.textFile(inputArg)
.flatMap(_.split("\t"))
.map((_,1))
.reduceByKey(_+_)
.saveAsTextFile(outArg)
上述是一個 WordCount 程式的邏輯執行計劃,每個階段會生成一個單獨的 RDD。
生成物理執行計劃
物理查詢計劃關注於底層資料狀態:
- 如圖一個 RDD 有 4個 Partition,那麼前三個 RDD 步驟作用於 RDD 迭代變換;這三個步驟可以劃分到一個 Stage,因為可以多個分割槽並行執行(生成 4 個任務分別排程到不同計算節點);
- 接著第四個 RDD 步驟需要進行 shuffle,所以需要單獨劃分 Stage(可以生成 3個任務排程到不同計算節點上執行);
任務排程與執行
將各個階段生成的 Task 排程到 Executor
上執行,執行過程中會不停向 Driver 彙報進度。