Spark企業級項目實戰,源碼深度剖析,實時流處理,機器學習

Spark(一): 基本架構及原理

 Apache Spark是一個圍繞速度、易用性和複雜分析構建的大數據處理框架,最初在2009年由加州大學伯克利分校的AMPLab開發,並於2010年成爲Apache的開源項目之一,與Hadoop和Storm等其他大數據和MapReduce技術相比,Spark有如下優勢:

  • Spark提供了一個全面、統一的框架用於管理各種有着不同性質(文本數據、圖表數據等)的數據集和數據源(批量數據或實時的流數據)的大數據處理的需求

  • 官方資料介紹Spark可以將Hadoop集羣中的應用在內存中的運行速度提升100倍,甚至能夠將應用在磁盤上的運行速度提升10倍

 目標:

  • 架構及生態

  • spark 與 hadoop

  • 運行流程及特點

  • 常用術語

  • standalone模式

  • yarn集羣

  • RDD運行流程

架構及生態:


  • 通常當需要處理的數據量超過了單機尺度(比如我們的計算機有4GB的內存,而我們需要處理100GB以上的數據)這時我們可以選擇spark集羣進行計算,有時我們可能需要處理的數據量並不大,但是計算很複雜,需要大量的時間,這時我們也可以選擇利用spark集羣強大的計算資源,並行化地計算,其架構示意圖如下:

  • Spark Core:包含Spark的基本功能;尤其是定義RDD的API、操作以及這兩者上的動作。其他Spark的庫都是構建在RDD和Spark Core之上的

  • Spark SQL:提供通過Apache Hive的SQL變體Hive查詢語言(HiveQL)與Spark進行交互的API。每個數據庫表被當做一個RDD,Spark SQL查詢被轉換爲Spark操作。

  • Spark Streaming:對實時數據流進行處理和控制。Spark Streaming允許程序能夠像普通RDD一樣處理實時數據

  • MLlib:一個常用機器學習算法庫,算法被實現爲對RDD的Spark操作。這個庫包含可擴展的學習算法,比如分類、迴歸等需要對大量數據集進行迭代的操作。

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

  • Spark架構的組成圖如下:

  • Cluster Manager:在standalone模式中即爲Master主節點,控制整個集羣,監控worker。在YARN模式中爲資源管理器

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

  • Driver: 運行Application 的main()函數

  • Executor:執行器,是爲某個Application運行在worker node上的一個進程

Spark與hadoop:


  • Hadoop有兩個核心模塊,分佈式存儲模塊HDFS和分佈式計算模塊Mapreduce

  • spark本身並沒有提供分佈式文件系統,因此spark的分析大多依賴於Hadoop的分佈式文件系統HDFS

  • Hadoop的Mapreduce與spark都可以進行數據計算,而相比於Mapreduce,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上運行,運行完釋放所有資源

     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採用了數據本地性和推測執行的優化機制

常用術語:


  • 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模式下,其進程名稱爲CoarseGrainedExecutor Backend。一個CoarseGrainedExecutor Backend有且僅有一個Executor對象, 負責將Task包裝成taskRunner,並從線程池中抽取一個空閒線程運行Task, 這個每一個oarseGrainedExecutor Backend能並行運行Task的數量取決與分配給它的cpu個數

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


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

    2. Apache Mesos:與hadoop MR兼容性良好的一種資源調度框架

    3. Hadoop Yarn: 主要是指Yarn中的ResourceManager

  • Worker: 集羣中任何可以運行Application代碼的節點,在Standalone模式中指的是通過slave文件配置的Worker節點,在Spark on Yarn模式下就是NoteManager節點

  • 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。下圖展示了TaskScheduler的作用

  • 在不同運行模式中任務調度器具體爲:


    1. Spark on Standalone模式爲TaskScheduler

    2. YARN-Client模式爲YarnClientClusterScheduler

    3. YARN-Cluster模式爲YarnClusterScheduler

  • 將這些術語串起來的運行層次圖如下:

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

Spark運行模式:


  • Spark的運行模式多種多樣,靈活多變,部署在單機上時,既可以用本地模式運行,也可以用僞分佈模式運行,而當以分佈式集羣的方式部署時,也有衆多的運行模式可供選擇,這取決於集羣的實際情況,底層的資源調度即可以依賴外部資源調度框架,也可以使用Spark內建的Standalone模式。

  • 對於外部資源調度框架的支持,目前的實現包括相對穩定的Mesos模式,以及hadoop YARN模式

  • 本地模式:常用於本地開發測試,本地還分別 local 和 local cluster

standalone: 獨立集羣運行模式


  • Standalone模式使用Spark自帶的資源調度框架

  • 採用Master/Slaves的典型架構,選用ZooKeeper來實現Master的HA

  • 框架結構圖如下:

  • 該模式主要的節點有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端上的

  • 運行過程如下圖:(參考至:http://blog.csdn.net/gamer_gyt/article/details/51833681

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

yarn:  (參考:http://blog.csdn.net/gamer_gyt/article/details/51833681)


  • 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將分兩個階段運行該應用程序:


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

    2. 第二個階段是由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申請註銷並關閉自己

Spark Client 和 Spark Cluster的區別:

  • 理解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模式下,Application Master僅僅向YARN請求Executor,Client會和請求的Container通信來調度他們工作,也就是說Client不能離開

思考: 我們在使用Spark提交job時使用的哪種模式?

 

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) ,每個任務都是數據和計算的合體。在進行下一階段前,當前階段的所有任務都要執行完成。因爲下一階段的第一個轉換一定是重新組織數據的,所以必須等當前階段所有結果數據都計算出來了才能繼續

作者:楊思義,2014年6月至今工作於北京亞信智慧數據科技有限公司 BDX大數據事業部,從2014年9月開始從事項目spark相關應用開發。

  來源:數盟

  Spark簡介

  Spark是整個BDAS的核心組件,是一個大數據分佈式編程框架,不僅實現了MapReduce的算子map 函數和reduce函數及計算模型,還提供更爲豐富的算子,如filter、join、groupByKey等。是一個用來實現快速而同用的集羣計算的平臺。

  Spark將分佈式數據抽象爲彈性分佈式數據集(RDD),實現了應用任務調度、RPC、序列化和壓縮,併爲運行在其上的上層組件提供API。其底層採用Scala這種函數式語言書寫而成,並且所提供的API深度借鑑Scala函數式的編程思想,提供與Scala類似的編程接口

  Sparkon Yarn

  

  從用戶提交作業到作業運行結束整個運行期間的過程分析。

  一、客戶端進行操作

  1. 根據yarnConf來初始化yarnClient,並啓動yarnClient

  2. 創建客戶端Application,並獲取Application的ID,進一步判斷集羣中的資源是否滿足executor和ApplicationMaster申請的資源,如果不滿足則拋出IllegalArgumentException;

  3. 設置資源、環境變量:其中包括了設置Application的Staging目錄、準備本地資源(jar文件、log4j.properties)、設置Application其中的環境變量、創建Container啓動的Context等;

  4. 設置Application提交的Context,包括設置應用的名字、隊列、AM的申請的Container、標記該作業的類型爲Spark;

  5. 申請Memory,並最終通過yarnClient.submitApplication向ResourceManager提交該Application。

  當作業提交到YARN上之後,客戶端就沒事了,甚至在終端關掉那個進程也沒事,因爲整個作業運行在YARN集羣上進行,運行的結果將會保存到HDFS或者日誌中。

  二、提交到YARN集羣,YARN操作

  1. 運行ApplicationMaster的run方法;

  2. 設置好相關的環境變量。

  3. 創建amClient,並啓動;

  4. 在Spark UI啓動之前設置Spark UI的AmIpFilter;

  5. 在startUserClass函數專門啓動了一個線程(名稱爲Driver的線程)來啓動用戶提交的Application,也就是啓動了Driver。在Driver中將會初始化SparkContext;

  6. 等待SparkContext初始化完成,最多等待spark.yarn.applicationMaster.waitTries次數(默認爲10),如果等待了的次數超過了配置的,程序將會退出;否則用SparkContext初始化yarnAllocator;

  7. 當SparkContext、Driver初始化完成的時候,通過amClient向ResourceManager註冊ApplicationMaster

  8. 分配並啓動Executeors。在啓動Executeors之前,先要通過yarnAllocator獲取到numExecutors個Container,然後在Container中啓動Executeors。

      那麼這個Application將失敗,將Application Status標明爲FAILED,並將關閉SparkContext。其實,啓動Executeors是通過ExecutorRunnable實現的,而ExecutorRunnable內部是啓動CoarseGrainedExecutorBackend的。

  9. 最後,Task將在CoarseGrainedExecutorBackend裏面運行,然後運行狀況會通過Akka通知CoarseGrainedScheduler,直到作業運行完成。

  Spark節點的概念

  一、Spark驅動器是執行程序中的main()方法的進程。它執行用戶編寫的用來創建SparkContext(初始化)、創建RDD,以及運行RDD的轉化操作和行動操作的代碼。

  驅動器節點driver的職責:

  1. 把用戶程序轉爲任務task(driver)

      Spark驅動器程序負責把用戶程序轉化爲多個物理執行單元,這些單元也被稱之爲任務task(詳解見備註)

  2. 爲執行器節點調度任務(executor)

      有了物理計劃之後,Spark驅動器在各個執行器節點進程間協調任務的調度。Spark驅動器程序會根據當前的執行器節點,把所有任務基於數據所在位置分配給合適的執行器進程。當執行任務時,執行器進程會把緩存的數據存儲起來,而驅動器進程同樣會跟蹤這些緩存數據的位置,並利用這些位置信息來調度以後的任務,以儘量減少數據的網絡傳輸。(就是所謂的移動計算,而不移動數據)。

  二、執行器節點

  作用:

  1. 負責運行組成Spark應用的任務,並將結果返回給驅動器進程;

  2. 通過自身的塊管理器(blockManager)爲用戶程序中要求緩存的RDD提供內存式存儲。RDD是直接緩存在執行器進程內的,因此任務可以在運行時充分利用緩存數據加快運算。

  驅動器的職責:

  所有的Spark程序都遵循同樣的結構:程序從輸入數據創建一系列RDD,再使用轉化操作派生成新的RDD,最後使用行動操作手機或存儲結果RDD,Spark程序其實是隱式地創建出了一個由操作組成的邏輯上的有向無環圖DAG。當驅動器程序執行時,它會把這個邏輯圖轉爲物理執行計劃。

  這樣 Spark就把邏輯計劃轉爲一系列步驟(stage),而每個步驟又由多個任務組成。這些任務會被打包送到集羣中。

  Spark初始化

  1. 每個Spark應用都由一個驅動器程序來發起集羣上的各種並行操作。驅動器程序包含應用的main函數,並且定義了集羣上的分佈式數據集,以及對該分佈式數據集應用了相關操作。

  2. 驅動器程序通過一個SparkContext對象來訪問spark,這個對象代表對計算集羣的一個連接。(比如在sparkshell啓動時已經自動創建了一個SparkContext對象,是一個叫做SC的變量。(下圖,查看變量sc)

      

  3. 一旦創建了sparkContext,就可以用它來創建RDD。比如調用sc.textFile()來創建一個代表文本中各行文本的RDD。(比如vallinesRDD = sc.textFile(“yangsy.text”),val spark = linesRDD.filter(line=>line.contains(“spark”),spark.count())

      執行這些操作,驅動器程序一般要管理多個執行器,就是我們所說的executor節點。

  4. 在初始化SparkContext的同時,加載sparkConf對象來加載集羣的配置,從而創建sparkContext對象。

      從源碼中可以看到,在啓動thriftserver時,調用了spark- daemon.sh文件,該文件源碼如左圖,加載spark_home下的conf中的文件。

      

      (在執行後臺代碼時,需要首先創建conf對象,加載相應參數, val sparkConf = newSparkConf().setMaster("local").setAppName("cocapp").set("spark.executor.memory","1g"), val sc: SparkContext = new SparkContext(sparkConf))

  RDD工作原理:

  RDD(Resilient DistributedDatasets)[1] ,彈性分佈式數據集,是分佈式內存的一個抽象概念,RDD提供了一種高度受限的共享內存模型,即RDD是隻讀的記錄分區的集合,只能通過在其他RDD執行確定的轉換操作(如map、join和group by)而創建,然而這些限制使得實現容錯的開銷很低。對開發者而言,RDD可以看作是Spark的一個對象,它本身運行於內存中,如讀文件是一個RDD,對文件計算是一個RDD,結果集也是一個RDD ,不同的分片、數據之間的依賴、key-value類型的map數據都可以看做RDD。

  主要分爲三部分:創建RDD對象,DAG調度器創建執行計劃,Task調度器分配任務並調度Worker開始運行。

  SparkContext(RDD相關操作)→通過(提交作業)→(遍歷RDD拆分stage→生成作業)DAGScheduler→通過(提交任務集)→任務調度管理(TaskScheduler)→通過(按照資源獲取任務)→任務調度管理(TaskSetManager)

  Transformation返回值還是一個RDD。它使用了鏈式調用的設計模式,對一個RDD進行計算後,變換成另外一個RDD,然後這個RDD又可以進行另外一次轉換。這個過程是分佈式的。

  Action返回值不是一個RDD。它要麼是一個Scala的普通集合,要麼是一個值,要麼是空,最終或返回到Driver程序,或把RDD寫入到文件系統中

  轉換(Transformations)(如:map, filter, groupBy, join等),Transformations操作是Lazy的,也就是說從一個RDD轉換生成另一個RDD的操作不是馬上執行,Spark在遇到Transformations操作時只會記錄需要這樣的操作,並不會去執行,需要等到有Actions操作的時候纔會真正啓動計算過程進行計算。

  操作(Actions)(如:count, collect, save等),Actions操作會返回結果或把RDD數據寫到存儲系統中。Actions是觸發Spark啓動計算的動因。

  它們本質區別是:Transformation返回值還是一個RDD。它使用了鏈式調用的設計模式,對一個RDD進行計算後,變換成另外一個RDD,然後這個RDD又可以進行另外一次轉換。這個過程是分佈式的。Action返回值不是一個RDD。它要麼是一個Scala的普通集合,要麼是一個值,要麼是空,最終或返回到Driver程序,或把RDD寫入到文件系統中。關於這兩個動作,在Spark開發指南中會有就進一步的詳細介紹,它們是基於Spark開發的核心。

  RDD基礎

  1. Spark中的RDD就是一個不可變的分佈式對象集合。每個RDD都被分爲多個分區,這些分區運行在集羣的不同節點上。創建RDD的方法有兩種:一種是讀取一個外部數據集;一種是在羣東程序裏分發驅動器程序中的對象集合,不如剛纔的示例,讀取文本文件作爲一個字符串的RDD的示例。

  2. 創建出來後,RDD支持兩種類型的操作:轉化操作和行動操作

      轉化操作會由一個RDD生成一個新的RDD。(比如剛纔的根據謂詞篩選)

      行動操作會對RDD計算出一個結果,並把結果返回到驅動器程序中,或把結果存儲到外部存儲系統(比如HDFS)中。比如first()操作就是一個行動操作,會返回RDD的第一個元素。

      注:轉化操作與行動操作的區別在於Spark計算RDD的方式不同。雖然你可以在任何時候定義一個新的RDD,但Spark只會惰性計算這些RDD。它們只有第一個在一個行動操作中用到時,纔會真正的計算。之所以這樣設計,是因爲比如剛纔調用sc.textFile(...)時就把文件中的所有行都讀取並存儲起來,就會消耗很多存儲空間,而我們馬上又要篩選掉其中的很多數據。

      這裏還需要注意的一點是,spark會在你每次對它們進行行動操作時重新計算。如果想在多個行動操作中重用同一個RDD,那麼可以使用RDD.persist()或RDD.collect()讓Spark把這個RDD緩存下來。(可以是內存,也可以是磁盤)

  3. Spark會使用譜系圖來記錄這些不同RDD之間的依賴關係,Spark需要用這些信息來按需計算每個RDD,也可以依靠譜系圖在持久化的RDD丟失部分數據時用來恢復所丟失的數據。(如下圖,過濾errorsRDD與warningsRDD,最終調用union()函數)

      

  RDD計算方式

  

  RDD的寬窄依賴

  

  窄依賴 (narrowdependencies) 和寬依賴 (widedependencies) 。窄依賴是指 父 RDD 的每個分區都只被子 RDD 的一個分區所使用 。相應的,那麼寬依賴就是指父 RDD 的分區被多個子 RDD 的分區所依賴。例如, map 就是一種窄依賴,而 join 則會導致寬依賴

  這種劃分有兩個用處。首先,窄依賴支持在一個結點上管道化執行。例如基於一對一的關係,可以在 filter 之後執行 map 。其次,窄依賴支持更高效的故障還原。因爲對於窄依賴,只有丟失的父 RDD 的分區需要重新計算。而對於寬依賴,一個結點的故障可能導致來自所有父 RDD 的分區丟失,因此就需要完全重新執行。因此對於寬依賴,Spark 會在持有各個父分區的結點上,將中間數據持久化來簡化故障還原,就像 MapReduce 會持久化 map 的輸出一樣。

  SparkExample

  

  步驟 1 :創建 RDD 。上面的例子除去最後一個 collect 是個動作,不會創建 RDD 之外,前面四個轉換都會創建出新的 RDD 。因此第一步就是創建好所有 RDD( 內部的五項信息 ) 。

  步驟 2 :創建執行計劃。Spark 會儘可能地管道化,並基於是否要重新組織數據來劃分 階段 (stage) ,例如本例中的 groupBy() 轉換就會將整個執行計劃劃分成兩階段執行。最終會產生一個 DAG(directedacyclic graph ,有向無環圖 ) 作爲邏輯執行計劃。

  步驟 3 :調度任務。 將各階段劃分成不同的 任務 (task) ,每個任務都是數據和計算的合體。在進行下一階段前,當前階段的所有任務都要執行完成。因爲下一階段的第一個轉換一定是重新組織數據的,所以必須等當前階段所有結果數據都計算出來了才能繼續。

  假設本例中的 hdfs://names 下有四個文件塊,那麼 HadoopRDD 中 partitions 就會有四個分區對應這四個塊數據,同時 preferedLocations 會指明這四個塊的最佳位置。現在,就可以創建出四個任務,並調度到合適的集羣結點上。

  Spark數據分區

  1. Spark的特性是對數據集在節點間的分區進行控制。在分佈式系統中,通訊的代價是巨大的,控制數據分佈以獲得最少的網絡傳輸可以極大地提升整體性能。Spark程序可以通過控制RDD分區方式來減少通訊的開銷。

  2. Spark中所有的鍵值對RDD都可以進行分區。確保同一組的鍵出現在同一個節點上。比如,使用哈希分區將一個RDD分成了100個分區,此時鍵的哈希值對100取模的結果相同的記錄會被放在一個節點上。

      (可使用partitionBy(newHashPartitioner(100)).persist()來構造100個分區)

  3. Spark中的許多操作都引入了將數據根據鍵跨界點進行混洗的過程。(比如:join(),leftOuterJoin(),groupByKey(),reducebyKey()等)對於像reduceByKey()這樣只作用於單個RDD的操作,運行在未分區的RDD上的時候會導致每個鍵的所有對應值都在每臺機器上進行本地計算。

  SparkSQL的shuffle過程

  

  Spark SQL的核心是把已有的RDD,帶上Schema信息,然後註冊成類似sql裏的”Table”,對其進行sql查詢。這裏面主要分兩部分,一是生成SchemaRD,二是執行查詢。

  如果是spark-hive項目,那麼讀取metadata信息作爲Schema、讀取hdfs上數據的過程交給Hive完成,然後根據這倆部分生成SchemaRDD,在HiveContext下進行hql()查詢。

  SparkSQL結構化數據

  1. 首先說一下ApacheHive,Hive可以在HDFS內或者在其他存儲系統上存儲多種格式的表。SparkSQL可以讀取Hive支持的任何表。要把Spark SQL連接已有的hive上,需要提供Hive的配置文件。hive-site.xml文件複製到spark的conf文件夾下。再創建出HiveContext對象(sparksql的入口),然後就可以使用HQL來對錶進行查詢,並以由行足證的RDD的形式拿到返回的數據。

  2. 創建Hivecontext並查詢數據

      importorg.apache.spark.sql.hive.HiveContext

      valhiveCtx = new org.apache.spark.sql.hive.HiveContext(sc)

      valrows = hiveCtx.sql(“SELECT name,age FROM users”)

      valfitstRow – rows.first()

      println(fitstRow.getSgtring(0)) //字段0是name字段

  3. 通過jdbc連接外部數據源更新與加載

      Class.forName("com.mysql.jdbc.Driver")

      val conn =DriverManager.getConnection(mySQLUrl)

      val stat1 =conn.createStatement()

      stat1.execute("UPDATE CI_LABEL_INFO set DATA_STATUS_ID = 2 , DATA_DATE ='" + dataDate +"' where LABEL_ID in ("+allCreatedLabels.mkString(",")+")")

      stat1.close()

      //加載外部數據源數據到內存

      valDIM_COC_INDEX_MODEL_TABLE_CONF =sqlContext.jdbc(mySQLUrl,"DIM_COC_INDEX_MODEL_TABLE_CONF").cache()

      val targets =DIM_COC_INDEX_MODEL_TABLE_CONF.filter("TABLE_DATA_CYCLE ="+TABLE_DATA_CYCLE).collect

  SparkSQL解析

  

  首先說下傳統數據庫的解析,傳統數據庫的解析過程是按Rusult、Data Source、Operation的次序來解析的。傳統數據庫先將讀入的SQL語句進行解析,分辨出SQL語句中哪些詞是關鍵字(如select,from,where),哪些是表達式,哪些是Projection,哪些是Data Source等等。進一步判斷SQL語句是否規範,不規範就報錯,規範則按照下一步過程綁定(Bind)。過程綁定是將SQL語句和數據庫的數據字典(列,表,視圖等)進行綁定,如果相關的Projection、Data Source等都存在,就表示這個SQL語句是可以執行的。在執行過程中,有時候甚至不需要讀取物理表就可以返回結果,比如重新運行剛運行過的SQL語句,直接從數據庫的緩衝池中獲取返回結果。在數據庫解析的過程中SQL語句時,將會把SQL語句轉化成一個樹形結構來進行處理,會形成一個或含有多個節點(TreeNode)的Tree,然後再後續的處理政對該Tree進行一系列的操作。

  Spark SQL對SQL語句的處理和關係數據庫對SQL語句的解析採用了類似的方法,首先會將SQL語句進行解析,然後形成一個Tree,後續如綁定、優化等處理過程都是對Tree的操作,而操作方法是採用Rule,通過模式匹配,對不同類型的節點採用不同的操作。SparkSQL有兩個分支,sqlContext和hiveContext。sqlContext現在只支持SQL語法解析器(Catalyst),hiveContext支持SQL語法和HiveContext語法解析器。


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