Spark任務執行流程隨筆

  當使用spark-submit提交一個作業之後,這個作業就會啓動一個對應的driver進程。
 
    根據你使用的部署模式(deploy-mode)不同,driver進程可能在本地啓動,也可能在集羣中某個工作節點上啓動。
 
    driver進程本身會根據我們設置的參數,佔有一定數量的內存和CPU core。而driver進程要做的第一件事,就是向集羣管理器(可以是Spark Standalone集羣,也可以是其他的資源管理集羣)申請運行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線程

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