一、Spark簡介
1.1 Spark是什么
Spark是一個通用的并行計算框架,由UCBerkeley的AMP實驗室開發。Spark基于map reduce 算法模式實現的分布式計算,擁有Hadoop MapReduce所具有的優點;但不同于Hadoop MapReduce的是Job中間輸出和結果可以保存在內存中,從而不再需要讀寫HDFS,節省了磁盤IO耗時,號稱性能比Hadoop快100倍。
1.2 Spark官網
http://spark.apache.org/
1.3 Spark架構及生態
(1)Spark Core:包含Spark的基本功能;尤其是定義RDD的API、操作以及這兩者上的動作。其他Spark的庫都是構建在RDD和Spark Core之上的
(2)Spark SQL:提供通過Apache Hive的SQL變體Hive查詢語言(HiveQL)與Spark進行交互的API。每個數據庫表被當做一個RDD,Spark SQL查詢被轉換為Spark操作。
(3)Spark Streaming:對實時數據流進行處理和控制。Spark Streaming允許程序能夠像普通RDD一樣處理實時數據
(4)MLlib:一個常用機器學習算法庫,算法被實現為對RDD的Spark操作。這個庫包含可擴展的學習算法,比如分類、回歸等需要對大量數據集進行迭代的操作。
(5)GraphX:控制圖、并行圖操作和計算的一組算法和工具的集合。GraphX擴展了RDD API,包含控制圖、創建子圖、訪問路徑上所有頂點的操作
1.4 Spark架構的組成圖
(1)Cluster Manager:在standalone模式中即為Master主節點,控制整個集群,監控worker。在YARN模式中為資源管理器
(2)Worker節點:從節點,負責控制計算節點,啟動Executor或者Driver。
(3)Driver: 運行Application 的main()函數
(4)Executor:執行器,是為某個Application運行在worker node上的一個進程
1.5 Spark三種集群模式
1.Standalone獨立集群
2.Mesos, apache mesos
3.Yarn, hadoop yarn
1.6 Spark與hadoop比較
(1)Hadoop有兩個核心模塊,分布式存儲模塊HDFS和分布式計算模塊Mapreduce
(2)spark本身并沒有提供分布式文件系統,因此spark的分析大多依賴于Hadoop的分布式文件系統HDFS
(3)Hadoop的Mapreduce與spark都可以進行數據計算,而相比于Mapreduce,spark的速度更快并且提供的功能更加豐富
1.7 Spark運行流程
spark運行流程圖如下:
(1)構建Spark Application的運行環境,啟動SparkContext
(2)SparkContext向資源管理器(可以是Standalone,Mesos,Yarn)申請運行Executor資源,并啟動StandaloneExecutorbackend,
(3)Executor向SparkContext申請Task
(4)SparkContext將應用程序分發給Executor
(5)SparkContext構建成DAG圖,將DAG圖分解成Stage、將Taskset發送給Task Scheduler,最后由Task Scheduler將Task發送給Executor運行
(6)Task在Executor上運行,運行完釋放所有資源
1.8 Spark運行特點
(1)每個Application獲取專屬的executor進程,該進程在Application期間一直駐留,并以多線程方式運行Task。這種Application隔離機制是有優勢的,無論是從調度角度看(每個Driver調度他自己的任務),還是從運行角度看(來自不同Application的Task運行在不同JVM中),當然這樣意味著Spark Application不能跨應用程序共享數據,除非將數據寫入外部存儲系統
(2)Spark與資源管理器無關,只要能夠獲取executor進程,并能保持相互通信就可以了
(3)提交SparkContext的Client應該靠近Worker節點(運行Executor的節點),最好是在同一個Rack里,因為Spark Application運行過程中SparkContext和Executor之間有大量的信息交換
(4)Task采用了數據本地性和推測執行的優化機制
1.9 Spark運行模式
(1)Spark的運行模式多種多樣,靈活多變,部署在單機上時,既可以用本地模式運行,也可以用偽分布模式運行,而當以分布式集群的方式部署時,也有眾多的運行模式可供選擇,這取決于集群的實際情況,底層的資源調度即可以依賴外部資源調度框架,也可以使用Spark內建的Standalone模式。
(2)對于外部資源調度框架的支持,目前的實現包括相對穩定的Mesos模式,以及hadoop YARN模式
(3)本地模式:常用于本地開發測試,本地還分別 local 和 local cluster
1.10 Spark特點
(1)運行速度快 => Spark擁有DAG執行引擎,支持在內存中對數據進行迭代計算。官方提供的數據表明,如果數據由磁盤讀取,速度是Hadoop MapReduce的10倍以上,如果數據從內存中讀取,速度可以高達100多倍。
(2)適用場景廣泛 => 大數據分析統計,實時數據處理,圖計算及機器學習
(3)易用性 => 編寫簡單,支持80種以上的高級算子,支持多種語言,數據源豐富,可部署在多種集群中
(4)容錯性高。Spark引進了彈性分布式數據集RDD (Resilient Distributed Dataset) 的抽象,它是分布在一組節點中的只讀對象集合,這些集合是彈性的,如果數據集一部分丟失,則可以根據“血統”(即充許基于數據衍生過程)對它們進行重建。另外在RDD計算時可以通過CheckPoint來實現容錯,而CheckPoint有兩種方式:CheckPoint Data,和Logging The Updates,用戶可以控制采用哪種方式來實現容錯。
1.11 Spark的適用場景
(1)目前大數據處理場景有以下幾個類型:
(2)復雜的批量處理(Batch Data Processing),偏重點在于處理海量數據的能力,至于處理速度可忍受,通常的時間可能是在數十分鐘到數小時;
(3)基于歷史數據的交互式查詢(Interactive Query),通常的時間在數十秒到數十分鐘之間
(4)基于實時數據流的數據處理(Streaming Data Processing),通常在數百毫秒到數秒之間
1.12 Spark基本概念
Application =>Spark的應用程序,包含一個Driver program和若干Executor
SparkContext => Spark應用程序的入口,負責調度各個運算資源,協調各個Worker Node上的Executor
Driver Program =>運行Application的main()函數并且創建SparkContext
Executor =>是為Application運行在Worker node上的一個進程,該進程負責運行Task,并且負責將數據存在內存或者磁盤上。每個Application都會申請各自的Executor來處理任務
Cluster Manager =>在集群上獲取資源的外部服務 (例如:Standalone、Mesos、Yarn)
Worker Node =>集群中任何可以運行Application代碼的節點,運行一個或多個Executor進程
Task =>運行在Executor上的工作單元
Job => SparkContext提交的具體Action操作,常和Action對應
Stage =>每個Job會被拆分很多組task,每組任務被稱為Stage,也稱TaskSet
RDD =>是Resilient distributed datasets的簡稱,中文為彈性分布式數據集;是Spark最核心的模塊和類
DAGScheduler =>根據Job構建基于Stage的DAG,并提交Stage給TaskScheduler
TaskScheduler =>將Taskset提交給Worker node集群運行并返回結果
Transformations =>是Spark API的一種類型,Transformation返回值還是一個RDD,所有的Transformation采用的都是懶策略,如果只是將Transformation提交是不會執行計算的
Action =>是Spark API的一種類型,Action返回值不是一個RDD,而是一個scala集合;計算只有在Action被提交的時候計算才被觸發。
二、Spark Core簡介
2.1 Spark Core之RDD
2.1.1 RDD是什么
RDD(Resilient Distributed Dataset)叫做彈性分布式數據集,是Spark中最基本的數據抽象,它代表一個不可變、可分區、里面的元素可并行計算的集合。RDD具有數據流模型的特點:自動容錯、位置感知性調度和可伸縮性。RDD允許用戶在執行多個查詢時顯式地將工作集緩存在內存中,后續的查詢能夠重用工作集,這極大地提升了查詢速度。
2.1.2 RDD的屬性
(1)partitions(分區):一組分片(Partition),即數據集的基本組成單位。對于RDD來說,每個分片都會被一個計算任務處理,并決定并行計算的粒度。用戶可以在創建RDD時指定RDD的分片個數,如果沒有指定,那么就會采用默認值。默認值就是程序所分配到的CPU Core的數目。
(2)partitioner(分區方法):一個計算每個分區的函數。Spark中RDD的計算是以分片為單位的,每個RDD都會實現compute函數以達到這個目的。compute函數會對迭代器進行復合,不需要保存每次計算的結果。
(3)dependencies(依賴關系):RDD之間的依賴關系。RDD的每次轉換都會生成一個新的RDD,所以RDD之間就會形成類似于流水線一樣的前后依賴關系。在部分分區數據丟失時,Spark可以通過這個依賴關系重新計算丟失的分區數據,而不是對RDD的所有分區進行重新計算。
(4)compute(獲取分區迭代列表):一個Partitioner,即RDD的分片函數。當前Spark中實現了兩種類型的分片函數,一個是基于哈希的HashPartitioner,另外一個是基于范圍的RangePartitioner。只有對于于key-value的RDD,才會有Partitioner,非key-value的RDD的Parititioner的值是None。Partitioner函數不但決定了RDD本身的分片數量,也決定了parent RDD Shuffle輸出時的分片數量。
(5)preferedLocations(優先分配節點列表) :一個列表,存儲存取每個Partition的優先位置(preferred location)。對于一個HDFS文件來說,這個列表保存的就是每個Partition所在的塊的位置。按照“移動數據不如移動計算”的理念,Spark在進行任務調度的時候,會盡可能地將計算任務分配到其所要處理數據塊的存儲位置。
2.1.3 RDD的創建方式
(1)通過讀取文件生成的
由外部存儲系統的數據集創建,包括本地的文件系統,還有所有Hadoop支持的數據集,比如HDFS、Cassandra、HBase等
scala> val file = sc.textFile("/spark/hello.txt")
(2)通過并行化的方式創建RDD
由一個已經存在的Scala集合創建。
scala> val array = Array(1,2,3,4,5)
array: Array[Int] = Array(1, 2, 3, 4, 5)
scala> val rdd = sc.parallelize(array)
rdd: org.apache.spark.rdd.RDD[Int] = ParallelCollectionRDD[27] at parallelize at <console>:26
(3)其他方式
讀取數據庫等等其他的操作。也可以生成RDD。RDD可以通過其他的RDD轉換而來的
2.1.4 RDD算子操作之Transformation
主要做的是就是將一個已有的RDD生成另外一個RDD。Transformation具有lazy特性(延遲加載)。Transformation算子的代碼不會真正被執行。只有當我們的程序里面遇到一個action算子的時候,代碼才會真正的被執行。這種設計讓Spark更加有效率地運行。
常用的Transformation:
2.1.4 RDD算子操作之Action
觸發代碼的運行,我們一段spark代碼里面至少需要有一個action操作。
常用的Action:
2.1.5 RDD依賴關系的本質內幕
由于RDD是粗粒度的操作數據集,每個Transformation操作都會生成一個新的RDD,所以RDD之間就會形成類似流水線的前后依賴關系;RDD和它依賴的父RDD(s)的關系有兩種不同的類型,即窄依賴(narrow dependency)和寬依賴(wide dependency)。如圖所示顯示了RDD之間的依賴關系。
從圖中可知:
窄依賴:是指每個父RDD的一個Partition最多被子RDD的一個Partition所使用,例如map、filter、union等操作都會產生窄依賴;(獨生子女)
寬依賴:是指一個父RDD的Partition會被多個子RDD的Partition所使用,例如groupByKey、reduceByKey、sortByKey等操作都會產生寬依賴;(超生)
需要特別說明的是對join操作有兩種情況:
(1)圖中左半部分join:如果兩個RDD在進行join操作時,一個RDD的partition僅僅和另一個RDD中已知個數的Partition進行join,那么這種類型的join操作就是窄依賴,例如圖1中左半部分的join操作(join with inputs co-partitioned);
(2)圖中右半部分join:其它情況的join操作就是寬依賴,例如圖1中右半部分的join操作(join with inputs not co-partitioned),由于是需要父RDD的所有partition進行join的轉換,這就涉及到了shuffle,因此這種類型的join操作也是寬依賴。
總結:
在這里我們是從父RDD的partition被使用的個數來定義窄依賴和寬依賴,因此可以用一句話概括下:如果父RDD的一個Partition被子RDD的一個Partition所使用就是窄依賴,否則的話就是寬依賴。因為是確定的partition數量的依賴關系,所以RDD之間的依賴關系就是窄依賴;由此我們可以得出一個推論:即窄依賴不僅包含一對一的窄依賴,還包含一對固定個數的窄依賴。
一對固定個數的窄依賴的理解:即子RDD的partition對父RDD依賴的Partition的數量不會隨著RDD數據規模的改變而改變;換句話說,無論是有100T的數據量還是1P的數據量,在窄依賴中,子RDD所依賴的父RDD的partition的個數是確定的,而寬依賴是shuffle級別的,數據量越大,那么子RDD所依賴的父RDD的個數就越多,從而子RDD所依賴的父RDD的partition的個數也會變得越來越多。
2.1.6 RDD依賴關系下的數據流視圖
在spark中,會根據RDD之間的依賴關系將DAG圖(有向無環圖)劃分為不同的階段,對于窄依賴,由于partition依賴關系的確定性,partition的轉換處理就可以在同一個線程里完成,窄依賴就被spark劃分到同一個stage中,而對于寬依賴,只能等父RDD shuffle處理完成后,下一個stage才能開始接下來的計算。
因此spark劃分stage的整體思路是:從后往前推,遇到寬依賴就斷開,劃分為一個stage;遇到窄依賴就將這個RDD加入該stage中。因此在圖2中RDD C,RDD D,RDD E,RDDF被構建在一個stage中,RDD A被構建在一個單獨的Stage中,而RDD B和RDD G又被構建在同一個stage中。
在spark中,Task的類型分為2種:ShuffleMapTask和ResultTask;
簡單來說,DAG的最后一個階段會為每個結果的partition生成一個ResultTask,即每個Stage里面的Task的數量是由該Stage中最后一個RDD的Partition的數量所決定的!而其余所有階段都會生成ShuffleMapTask;之所以稱之為ShuffleMapTask是因為它需要將自己的計算結果通過shuffle到下一個stage中;也就是說上圖中的stage1和stage2相當于mapreduce中的Mapper,而ResultTask所代表的stage3就相當于mapreduce中的reducer。
在之前動手操作了一個wordcount程序,因此可知,Hadoop中MapReduce操作中的Mapper和Reducer在spark中的基本等量算子是map和reduceByKey;不過區別在于:Hadoop中的MapReduce天生就是排序的;而reduceByKey只是根據Key進行reduce,但spark除了這兩個算子還有其他的算子;因此從這個意義上來說,Spark比Hadoop的計算算子更為豐富。
2.2 Spark Core之Job
spark中對RDD的操作類型分為transformation和action,其中transformation是一種延遲執行的操作,并不會立即執行而是返回一個含有依賴關系的RDD,例如map、filter、sortBy、flatMap等操作,當調用action操作時,** spark會通過DAGScheduler構建一個job的執行拓撲,包括多個stage和task,所有的stage和task構成了這個action觸發的job,最后提交到集群中執行。
2.3 Spark Core之Stage
Job是由stage構成的,spark中的stage只有兩種,shuffleMapStage和resultStage。Job劃分Stage的依據是shuffle與否(即依賴的類型),當DagScheduler進行DAG執行圖構建時,遇到一個shuffle dependency會生成一個shuffle map stage,調用鏈最后一個shuffle reduce端將生成為result stage 也叫 final stage.
handleJobSubmitted->submitStage(finalStage)->遞歸調用submitStage,如果當前提交的stage沒有parent stage則直接提交taskSet,否則將當前stage加入waiting ?stage列表,每當觸發某些事件時(MapStageSubmitted、TaskCompletion..)都會進行一次 waiting stages 的提交。
2.4 Spark Core之Task
task是邏輯的具體執行單元,stage由task構成,當提交stage時,DAGScheduler會根據當前stage的類型序列化出不同類型的task并進行broadcast,如果是shuffleMapStage則序列化出ShuffleMapTask,如果是resultStage則序列化出ResultTask,其中task的數量和當前stage所依賴的RDD的partition的數量是一致的,Task作用的數據單元是partition,即每個task只處理一個partition的數據。
2.5 Spark Core之shuffle
shuffle是影響spark性能的核心所在,和mapreduce中的shuffle概念類似,在spark2.0中shuffleManager只有sortShuffleManager,并且在滿足一定條件下可以使用Serialized sorting 即在tungsten中對其進行的優化
2.6 Spark Core之Tungsten
該項目主要是為了讓spark計算模型能更好的利用硬件性能,主要包含三部分:
1、內存管理與二進制處理
2、cache-aware
3、代碼生成
三、Spark Streaming簡介
3.1 Spark Streaming概述
Spark Streaming是Spark核心API的一個擴展,可以實現高吞吐量的、具備容錯機制的實時流數據的處理。支持從多種數據源獲取數據,包括Kafk、Flume、Twitter、ZeroMQ、Kinesis 以及TCP sockets,從數據源獲取數據之后,可以使用諸如map、reduce、join和window等高級函數進行復雜算法的處理。最后還可以將處理結果存儲到文件系統,數據庫和現場儀表盤。在“One Stack rule them all”的基礎上,還可以使用Spark的其他子框架,如集群學習、圖計算等,對流數據進行處理。
Spark Streaming處理的數據流圖:
3.2 Spark Streaming架構
SparkStreaming是一個對實時數據流進行高通量、容錯處理的流式處理系統,可以對多種數據源(如Kdfka、Flume、Twitter、Zero和TCP?套接字)進行類似Map、Reduce和Join等復雜操作,并將結果保存到外部文件系統、數據庫或應用到實時儀表盤。
l計算流程:Spark Streaming是將流式計算分解成一系列短小的批處理作業。這里的批處理引擎是Spark Core,也就是把Spark Streaming的輸入數據按照batch size(如1秒)分成一段一段的數據(Discretized Stream),每一段數據都轉換成Spark中的RDD(Resilient Distributed Dataset),然后將Spark Streaming中對DStream的Transformation操作變為針對Spark中對RDD的Transformation操作,將RDD經過操作變成中間結果保存在內存中。整個流式計算根據業務的需求可以對中間的結果進行疊加或者存儲到外部設備。下圖顯示了Spark Streaming的整個流程。
l容錯性:對于流式計算來說,容錯性至關重要。首先我們要明確一下Spark中RDD的容錯機制。每一個RDD都是一個不可變的分布式可重算的數據集,其記錄著確定性的操作繼承關系(lineage),所以只要輸入數據是可容錯的,那么任意一個RDD的分區(Partition)出錯或不可用,都是可以利用原始輸入數據通過轉換操作而重新算出的。
對于Spark Streaming來說,其RDD的傳承關系如下圖所示,圖中的每一個橢圓形表示一個RDD,橢圓形中的每個圓形代表一個RDD中的一個Partition,圖中的每一列的多個RDD表示一個DStream(圖中有三個DStream),而每一行最后一個RDD則表示每一個Batch Size所產生的中間結果RDD。我們可以看到圖中的每一個RDD都是通過lineage相連接的,由于Spark Streaming輸入數據可以來自于磁盤,例如HDFS(多份拷貝)或是來自于網絡的數據流(Spark Streaming會將網絡輸入數據的每一個數據流拷貝兩份到其他的機器)都能保證容錯性,所以RDD中任意的Partition出錯,都可以并行地在其他機器上將缺失的Partition計算出來。這個容錯恢復方式比連續計算模型(如Storm)的效率更高。
l實時性:對于實時性的討論,會牽涉到流式處理框架的應用場景。Spark Streaming將流式計算分解成多個Spark Job,對于每一段數據的處理都會經過Spark DAG圖分解以及Spark的任務集的調度過程。對于目前版本的Spark Streaming而言,其最小的Batch Size的選取在0.5~2秒鐘之間(Storm目前最小的延遲是100ms左右),所以Spark Streaming能夠滿足除對實時性要求非常高(如高頻實時交易)之外的所有流式準實時計算場景。
l擴展性與吞吐量:Spark目前在EC2上已能夠線性擴展到100個節點(每個節點4Core),可以以數秒的延遲處理6GB/s的數據量(60M records/s),其吞吐量也比流行的Storm高2~5倍,圖4是Berkeley利用WordCount和Grep兩個用例所做的測試,在Grep這個測試中,Spark Streaming中的每個節點的吞吐量是670k records/s,而Storm是115k records/s。
3.3 Spark Streaming之StreamingContext
有兩種創建StreamingContext的方式:
appName,是用來在Spark UI上顯示的應用名稱。master,是一個Spark、Mesos或者Yarn 集群的URL,或者是local[*]。
batch interval可以根據你的應用程序的延遲要求以及可用的集群資源情況來設置。
一個StreamingContext定義之后,必須做以下幾件事情:
1、通過創建輸入DStream來創建輸入數據源。
2、通過對DStream定義transformation和output算子操作,來定義實時計算邏輯。
3、調用StreamingContext的start()方法,來開始實時處理數據。
4、調用StreamingContext的awaitTermination()方法,來等待應用程序的終止。可以使用CTRL+C手動停止,或者就是讓它持續不斷的運行進行計算。
5、也可以通過調用StreamingContext的stop()方法,來停止應用程序。
StreamingContext需要注意的要點:
1、只要一個StreamingContext啟動之后,就不能再往其中添加任何計算邏輯了。比如執行start()方法之后,還給某個DStream執行一個算子。
2、一個StreamingContext停止之后,是肯定不能夠重啟的。調用stop()之后,不能再調用start()
3、一個JVM同時只能有一個StreamingContext啟動。在你的應用程序中,不能創建兩個StreamingContext。
4、調用stop()方法時,會同時停止內部的SparkContext,如果不希望如此,還希望后面繼續使用SparkContext創建其他類型的Context,比如SQLContext,那么就用stop(false)。
5、一個SparkContext可以創建多個StreamingContext,只要上一個先用stop(false)停止,再創建下一個即可。
3.4 Spark Streaming之DStream
DStream(Discretized Stream)作為Spark Streaming的基礎抽象,它代表持續性的數據流。這些數據流既可以通過外部輸入源賴獲取,也可以通過現有的Dstream的transformation操作來獲得。在內部實現上,DStream由一組時間序列上連續的RDD來表示。每個RDD都包含了自己特定時間間隔內的數據流。如圖7-3所示。
對DStream中數據的各種操作也是映射到內部的RDD上來進行的,如圖7-4所示,對Dtream的操作可以通過RDD的transformation生成新的DStream。這里的執行引擎是Spark。
3.5 Spark Streaming之Input DStream and Receivers
如同Storm的Spout,Spark Streaming需要指明數據源。如上例所示的socketTextStream,Spark Streaming以socket連接作為數據源讀取數據。當然Spark Streaming支持多種不同的數據源,包括Kafka、 Flume、HDFS/S3、Kinesis和Twitter等數據源;
3.6 Spark Streaming之Transformations
https://www.cnblogs.com/yhl-yh/p/7651558.html
3.7 Spark Streaming之Operations
https://www.cnblogs.com/yhl-yh/p/7651558.html
3.8 Spark Streaming之Join Operations
(1)DStream對象之間的Join
這種join一般應用于窗口函數形成的DStream對象之間,具體可以參考第一部分中的join操作,除了簡單的join之外,還有leftOuterJoin, rightOuterJoin和fullOuterJoin。
(2)DStream和dataset之間的join
這一種join,可以參考前面transform操作中的示例。
3.9 Spark Streaming之Output Operations
https://www.cnblogs.com/yhl-yh/p/7651558.html
四、Spark SQL簡介
4.1 Spark SQL是什么
是Spark用來處理結構化數據的一個模塊,它提供了一個編程抽象叫做DataFrame,并且作為分布式SQL查詢引擎的作用
4.2 Spark SQL基礎數據模型
DataFrame是由“命名列”(類似關系表的字段定義)所組織起來的一個分布式數據集合。你可以把它看成是一個關系型數據庫的表。
DataFrame可以通過多種來源創建:結構化數據文件,hive的表,外部數據庫,或者RDDs
4.3 Spark SQL運行流程
1、將SQL語句通過詞法和語法解析生成未綁定的邏輯執行計劃(Unresolved LogicalPlan),包含Unresolved Relation、Unresolved Function和Unresolved Attribute,然后在后續步驟中使用不同的Rule應用到該邏輯計劃上
2、Analyzer使用Analysis Rules,配合元數據(如SessionCatalog 或是 Hive Metastore等)完善未綁定的邏輯計劃的屬性而轉換成綁定的邏輯計劃。具體流程是縣實例化一個Simple Analyzer,然后遍歷預定義好的Batch,通過父類Rule Executor的執行方法運行Batch里的Rules,每個Rule會對未綁定的邏輯計劃進行處理,有些可以通過一次解析處理,有些需要多次迭代,迭代直到達到FixedPoint次數或前后兩次的樹結構沒變化才停止操作。
3、Optimizer使用Optimization Rules,將綁定的邏輯計劃進行合并、列裁剪和過濾器下推等優化工作后生成優化的邏輯計劃
4、Planner使用Planning Strategies,對優化的邏輯計劃進行轉換(Transform)生成可以執行的物理計劃。根據過去的性能統計數據,選擇最佳的物理執行計劃CostModel,最后生成可以執行的物理執行計劃樹,得到SparkPlan
5、在最終真正執行物理執行計劃之前,還要進行preparations規則處理,最后調用SparkPlan的execute執行計算RDD。
4.4 Spark SQL的誕生
但是,隨著Spark的發展,對于野心勃勃的Spark團隊來說,Shark對于Hive的太多依賴(如采用Hive的語法解析器、查詢優化器等等),制約了Spark的One Stack Rule Them All的既定方針,制約了Spark各個組件的相互集成,所以提出了SparkSQL項目。SparkSQL拋棄原有Shark的代碼,汲取了Shark的一些優點,如內存列存儲(In-Memory Columnar Storage)、Hive兼容性等,重新開發了SparkSQL代碼;由于擺脫了對Hive的依賴性,SparkSQL無論在數據兼容、性能優化、組件擴展方面都得到了極大的方便,真可謂“退一步,海闊天空”。
4.5 Spark SQL和Hive On Spark的區別
很多人誤以為Spark SQL和 Hive On Spark是一個東西,其實不然,這里對二者做一個簡單的介紹:Spark SQL的前身是Shark, Shark 剛開始也是使用了hive里面一些東西的,但隨著后來的發展,Shark始終不能很好的支持Hive里面的一些功能,這是因為在版本的更迭和Hive的多年發展,有些東西并不能始終支持,Shark 終止后,SparkSQL作為Spark生態的一員繼續發展,而不再受限于Hive,只是兼容Hive;而Hive On Spark 是Hive 想要將底層的執行引擎在Spark上做很好的支持,并且想要對Hive的底層執行引擎做更多的支持.
4.6 Spark SQL架構
SparkSQL架構分為前,后,中,三個部分,其中中間部分的Catalyst optimizer是架構的核心,也是SparkSQL 優化的關鍵所在。
4.7 Spark SQL出現原因
(1)使用SQL來進行大數據處理,這本身就為傳統的RDBMS人員降低了要求,因為這不需要使用人員掌握像mapreduce的編程方法。
(2)SparkSQL 可以支持SQL API,DataFrame和Dataset API多種API,這意味著開發人員可以采用DataFrame和Dataset API 進行編程,這和采用Spark core 的RDD API 進行編程有很大的不同,首先,并不像使用RDD進行編程時,開發人員在采用不同的編程語言和不同的方式開發應用程序時,其應用程序的性能千差萬別,但如果使用DataFrame和Dataset進行開發時,資深開發人員和初級開發人員開發的程序性能差異很小,這是因為SparkSQL 使用Catalyst optimizer 對執行計劃做了很好的優化。
(3)其次,SparkSQL既然對底層使用了很好的優化方式,這就讓開發人員在不熟悉底層優化方式時也可以進行很好的程序開發,降低了開發人員對程序開發的門檻。
(4)最后,SparkSQL 對外部數據源(External Datasource)做了很好的支持,像對關系型數據庫,如mysql,可以使用外部數據源的方式使用SparkSQL對數據庫里面的數據直接進行處理,而不需要像以前使用sqoop導入導出。
4.8 Spark SQL如何使用
首先,利用sqlContext從外部數據源加載數據為DataFrame
然后,利用DataFrame上豐富的api進行查詢、轉換
最后,將結果進行展現或存儲為各種外部數據形式
4.9 Spark SQL愿景
1.寫更少的代碼
2.讀更少的數據
3.把優化的工作交由底層的優化器運行
4.14 SparkContext運行原理分析
前面我們隊Spark SQL運行架構進行分析,知道從輸入SQL語句到生成Dataset分為5個步驟,但實際運行過程中在輸入SQL語句之前,Spark還有加載SessionCatalog步驟。
4.14.1 使用SessionCatalog保存元數據
在解析SQL語句之前需要初始化SQLContext,它定義了Spark SQL執行的上下文,并把元數據保存在SessionCatalog中,這些元數據包括表名稱、表字段名稱和字段類型等。
SessionCatalog中保存的是表名和邏輯執行計劃對應的哈希列表,這些數據將在解析未綁定的邏輯計劃上使用。(SessionCatalog中的表名對應的邏輯執行計劃是什么?是這個Dataset對應的邏輯執行計劃)
4.14.2 使用Antlr生成未綁定的邏輯計劃
Spark 2.0版本起使用Antlr進行詞法和語法解析。使用Antlr生成未綁定的邏輯計劃分為兩個階段:第一階段的過程為詞法分析(Lexical Analysis),負責將符號(Token)分組成符號類(Token class or Token type),第二階段就是真正的Parser,默認Antlr會構建出一顆分析樹(Parser Tree)或者叫語法樹(Syntax
Tree)。
SQLContext類中定義了SQL的解析方法parseSql。具體的SQL解析在AbastrctSqlParser抽象類中的parse方法進行,解析完畢后生成語法樹,語法樹會根據系統初始化的AstBuilder解析生成表達式、邏輯計劃或表標識對象。
在AbstractSqlParse的parse方法中,先實例化詞法解析器SqlBaseLexer和語法解析器SqlBaseParser,然后嘗試用Antlr較快的解析模式SLL,如果解析失敗,則會再嘗試使用普通解析模型LL,解析完畢后返回解析結果。
4.14.3 使用Analyzer綁定邏輯執行計劃
該階段Analyzer使用Analysis Rules,結合SessionCatalog元數據,對未綁定的邏輯計劃進行解析,生成了已綁定的邏輯計劃。在該處理過程中,先實例化一個Analyzer,在Analyzer中定義了FiexedPoint和Seq[Batch]兩個變量,其中FixedPoint為迭代次數的上線,而Seq[Batch]為所定義需要執行批處理的序列,每個批處理由一系列Rule和策略所組成,策略一般分為Once和FixedPoint(可理解為迭代次數)
4.14.4 使用Optimizer優化邏輯計劃
Optimizer的實現和處理方式同Analyzer類似,在該類中定義一系列Rule并同樣繼承于RuleExecutor。利用這些Rule對邏輯計劃和Expression進行迭代處理,從而達到對樹的節點進行合并和優化。其中主要的優化策略總結起來是合并、列裁剪和過濾器下推等幾大類。
Optimizer的優化策略不僅對已綁定的邏輯計劃進行優化,而且對邏輯計劃中的Expression也進行了優化,其原理就是遍歷樹,然后應用優化Rule,但是注意一點,對邏輯計劃處理時先序遍歷,而對Expression的處理是后續遍歷(根據樹節點類型來判斷是邏輯計劃還是Expression嗎?)
4.14.5 使用SparkPlanner生成可執行物理計劃
SparkPlanner使用Planning Strategies對優化的邏輯計劃進行轉換(Transform),生成可以執行的物理計劃。在QueryExecution類代碼中,調用SparkPlanner.plan方法對優化的邏輯計劃進行處理,而SparkPlanner并未定義plan方法,實際是調用SparkPlanner的祖父類QueyrPlanner的plan方法,然后會返回一個Iterator[Physicalplan]。
SparkPlanner繼承于SparkStrategies,而SparkStrategies繼承了QueryPlanner。其中SparkStrategies包含了一系列特定的Strategies,這些Strategies是繼承自QueryPlanner中定義的GenericStrategy。在SparkPlanner通過改寫祖父類QueryPlanner中strategies策略變量,在該變量中定義了轉換成物理計劃所執行的策略。
4.14.6 使用QueryExecution執行物理計劃
在該步驟中先調用SparkPlanner.preparations對物理計劃進行準備工作,規范返回數據行的格式等,然后調用SparkPlan.execute執行物理計劃,從數據庫中查詢數據并生成RDD。
SparkPlan的preparations其實是一個RuleExecutor[SparkPlan],它會調用RuleExecutor的execut方法對前面生成的物理計劃應用Rule進行匹配,最終生成一個SparkPlan。
SparkPlan繼承于QueryPlan,SparkPlan中定義了SQL語句執行的execute方法,執行完execute方法返回的是一個RDD,之后可以運行Spark作業對該RDD進行操作。
4.15 Spark SQL小結
1)Spark SQL的應用并不局限于SQL;
2)訪問hive、json、parquet等文件的數據;
3)SQL只是Spark SQL的一個功能而已;
4)Spark SQL提供了SQL的api、DataFrame和Dataset的API;
五、SparkMLlib簡介
5.1 SparkMLlib是什么
MLlib是Spark的機器學習(Machine Learning)庫,旨在簡化機器學習的工程實踐工作,并方便擴展到更大規模。MLlib由一些通用的學習算法和工具組成,包括分類、回歸、聚類、協同過濾、降維等,同時還包括底層的優化原語和高層的管道API。具體來說,其主要包括以下幾方面的內容:
(1)算法工具:常用的學習算法,如分類、回歸、聚類和協同過濾;
(2)特征化公交:特征提取、轉化、降維,和選擇公交;
(3)管道(Pipeline):用于構建、評估和調整機器學習管道的工具;
(4)持久性:保存和加載算法,模型和管道;
(5)實用工具:線性代數,統計,數據處理等工具。
5.2 SparkMLlib調優
MLlib是spark的可以擴展的機器學習庫,由以下部分組成:通用的學習算法和工具類,包括分類,回歸,聚類,協同過濾,降維,當然也包括調優的部分
Data
?typesBasic
statistics(基本統計)
summary statistics概括統計correlations 相關性stratified sampling 分層取樣hypothesis testing 假設檢驗random data generation 隨機數生成Classification
and regression(分類一般針對離散型數據而言的,回歸是針對連續型數據的。本質上是一樣的)
linear
models (SVMs, logistic regression, linear regression)線性模型(支持向量機,邏輯回歸,線性回歸)naive
Bayes貝葉斯算法decision
trees決策樹ensembles
of trees(Random Forests and Gradient-Boosted Trees)多種樹(隨機森林和梯度增強樹)Collaborative
filtering協同過濾
alternating least squares (ALS)(交替最小二乘法(ALS) )Clustering
聚類
k-means k均值算法Dimensionality
reduction (降維)
singular value decomposition (SVD)奇異值分解principal component analysis (PCA)主成分分析Feature
extraction and transformation特征提取和轉化Optimization (developer)優化部分
stochastic gradient descent隨機梯度下降limited-memory BFGS (L-BFGS) 短時記憶的BFGS (擬牛頓法中的一種,解決非線性問題)
六、SparkGraphX簡介
6.1 Spark GraphX是什么
Spark GraphX是一個分布式圖處理框架,它是基于Spark平臺提供對圖計算和圖挖掘簡潔易用的而豐富的接口,極大的方便了對分布式圖處理的需求。
6.2 Spark GraphX的框架
設計GraphX時,點分割和GAS都已成熟,在設計和編碼中針對它們進行了優化,并在功能和性能之間尋找最佳的平衡點。如同Spark本身,每個子模塊都有一個核心抽象。GraphX的核心抽象是Resilient Distributed Property Graph,一種點和邊都帶屬性的有向多重圖。它擴展了Spark RDD的抽象,有Table和Graph兩種視圖,而只需要一份物理存儲。兩種視圖都有自己獨有的操作符,從而獲得了靈活操作和執行效率。
如同Spark,GraphX的代碼非常簡潔。GraphX的核心代碼只有3千多行,而在此之上實現的Pregel模式,只要短短的20多行。GraphX的代碼結構整體下圖所示,其中大部分的實現,
都是圍繞Partition的優化進行的。這在某種程度上說明了點分割的存儲和相應的計算優化,的確是圖計算框架的重點和難點。
6.3 Spark GraphX實現分析
如同Spark本身,每個子模塊都有一個核心抽象。GraphX的核心抽象是Resilient Distributed Property Graph,一種點和邊都帶屬性的有向多重圖。它擴展了Spark RDD的抽象,有Table和Graph兩種視圖,而只需要一份物理存儲。兩種視圖都有自己獨有的操作符,從而獲得了靈活操作和執行效率。
GraphX的底層設計有以下幾個關鍵點。
對Graph視圖的所有操作,最終都會轉換成其關聯的Table視圖的RDD操作來完成。這樣對一個圖的計算,最終在邏輯上,等價于一系列RDD的轉換過程。因此,Graph最終具備了RDD的3個關鍵特性:Immutable、Distributed和Fault-Tolerant,其中最關鍵的是Immutable(不變性)。邏輯上,所有圖的轉換和操作都產生了一個新圖;物理上,GraphX會有一定程度的不變頂點和邊的復用優化,對用戶透明。
兩種視圖底層共用的物理數據,由RDD[Vertex-Partition]和RDD[EdgePartition]這兩個RDD組成。點和邊實際都不是以表Collection[tuple]的形式存儲的,而是由VertexPartition/EdgePartition在內部存儲一個帶索引結構的分片數據塊,以加速不同視圖下的遍歷速度。不變的索引結構在RDD轉換過程中是共用的,降低了計算和存儲開銷。
圖的分布式存儲采用點分割模式,而且使用partitionBy方法,由用戶指定不同的劃分策略(PartitionStrategy)。劃分策略會將邊分配到各個EdgePartition,頂點Master分配到各個VertexPartition,EdgePartition也會緩存本地邊關聯點的Ghost副本。劃分策略的不同會影響到所需要緩存的Ghost副本數量,以及每個EdgePartition分配的邊的均衡程度,需要根據圖的結構特征選取最佳策略。目前有EdgePartition2d、EdgePartition1d、RandomVertexCut和CanonicalRandomVertexCut這四種策略。