Spark介紹
- 一個分佈式的並行計算框架
- spark是下一代的map-reduce,擴展了mr的數據處理流程
- executor都是裝載在container裏運行,container默認內存是1G(參數yarn.scheduler.minimum-allocation-mb定義)
- AM(Application Master)在Spark中叫driver,AM向RM申請的是executor資源,當分配完資源後,executor啓動後,由spark的AM向
- executor分配task,分配多少task、分配到哪個executor由AM決定,可理解爲spark也有個調度過程,這些task都運行在executor的坑裏
- Executor有線程池多線程管理這些坑內的task
Executor伴隨整個app的生命週期(申請的進程的內存一直到任務結束)
線程池模型,省去進程頻繁啓停的開銷
MapReduce的問題
調度慢,啓動map、reduce太耗時
計算慢,每一步都要保存中間結果落磁盤
API抽象簡單,只有map和reduce兩個原語(需要通過腳本把map和reduce串起來)
缺乏作業流描述,一項任務需要多輪mr
具體描述:
MR中 : 啓動map進程,啓動reduce進程,所以啓動慢
spark:先啓動executor進程,再啓動線程:比如10個線程:8 map task(線程)2 reduce(線程)
MR:map->reduce的中間結果在磁盤
spark:map->reduce的中間結果也在磁盤(默認)
除非,進行cache(它調用的是persist)默認在內存中
spark只需要通過代碼就能把很多的數據處理串在一起:
df = spark.map.reduce
df1 = df.groupBy().count()
通過spark shell的方式查看具體的數據
Spark解決的問題
– 最大化利用內存cache(內存計算下,Spark比MR快100倍)
– 中間結果放內存,加速迭代
input --> iter1 --> 分佈式內存 --> iter2 分佈式內存
– 某結果集放內存,加速後續查詢和處理,解決運行慢的問題
input --> 分佈式內存 --> query1,query2,...
- 豐富的API(解決API單一問題)
包括Transformations和Action
Transfomation變換的api,比如map可對每一行做變換,filter過濾出符合條件的行等,這些API實現用戶算法,靈活。
spark提供很多轉換和動作,很多基本操作如Join,GroupBy已經在RDD轉換和動作中實現。不需用戶自己實現。寫操作就是action:寫磁盤,寫內存
cache屬於transformation,df.cache告訴任務df需要執行之後放到內存中
- 完整作業描述,將用戶的整個作業穿起來。關鍵是這3行。可以立即解釋。不像mr那樣,需要實現多個map和reduce腳本,解決MR缺乏作業流描述問題
val file = sc.textFile(hdfs://input)
val counts = file.flatMap(
line => line.split(" "))
.map(word => (word, 1))
.reduceByKey(_ + _)
counts.saveAsTextFile(hdfs://output)
Spark核心
• Spark基於彈性分佈式數據集(RDD)模型。
• RDD( Resilient Distributed Dataset ):彈性分佈式數據集(相當於集合),它的本質是數據
集的描述(只讀的、可分區的分佈式數據集),而不是數據集本身
• RDD的關鍵特徵:
RDD使用戶能夠顯式將計算結果保存在內存中,控制數據的劃分
使用更豐富的操作來處理,只讀(由一個RDD變換得到另一個RDD,但是不能對本身的RDD修改)
記錄數據的變換而不是數據本身保證容錯(lineage)
• RDD採用數據應用變換(map,filter,join),若部分數據丟失,RDD擁有足夠的信息得知這部分數據是如何計算得到的,
可通過重新計算來得到丟失的數據
容錯:1.備份數據 2.記錄更新方式
變換方式:rdd1->rdd2->rdd3
可以知道:rdd2 = rdd1->rdd2
• 恢復數據方法很快,無需大量數據複製操作,可以認爲Spark是基於RDD模型的系統
– 懶操作,延遲計算,action的時候才操作
– 瞬時性,用時才產生,用完就釋放
RDD來源:
– 讀取數據,如從HDFS中讀數據構建RDD(Sparkcontext (sc)是spark的入口,編寫spark程序用到
的第一個類,包含sparkconf sparkenv等類)
– 通過別的RDD轉換過來
val b = a.map(x => (x, 1))
– 定義一個scala數組
val c = sc.parallelize(1 to 10, 1)
– 有一個已經存在的RDD通過持久化操作生成
val d = a.persist(), a.saveAsHadoopFile(“/xxx/xxx/”)
- Dataframe轉換過來,這樣就可以從hive過來數據
Spark針對RDD提供的操作:transformations和action
- transformations(如上文)是RDD之間的變換,action會對數據執行一定的操作
- transformations採用懶策略,僅在對相關RDD進行action提交時才觸發計算
RDD分區和依賴:
• 每個RDD包含了數據分塊/分區(partition)的集合,每個partition是不可分割的
– 實際數據塊的描述(實際數據到底存在哪,或者不存在)
– 其值依賴於哪些partition
寬依賴和窄依賴:(rddA=>rddB)
寬依賴: B的每個partition依賴於A的所有partition(需要用到之前所有的數據)
如groupByKey、reduceByKey、join……,由A產生B時會先對A做shuffle分桶
窄依賴: B的每個partition依賴於A的常數個partition(需要用到之前部分的數據)
如map、filter、union等 (圖片來自網絡)
• 從後往前,將寬依賴的邊刪掉,連通分量及其在原圖中所有依賴的RDD,構成一個stage
• DAG是在計算過程中不斷擴展 ,在action後纔會 啓動計算
• 每個stage內部儘可能多地包含一組具有窄依賴關係的轉換,並將它們流水線並行化(pipeline)
• 從後往前,將寬依賴的邊刪掉,連通分量及其在原圖中所有依賴的RDD,構成一個stage
• DAG是在計算過程中不斷擴展 ,在action後纔會 啓動計算
• 每個stage內部儘可能多地包含一組具有窄依賴關係的轉換,並將它們流水線並行化(pipeline)
• 每個partition的計算就是一個task,task是調度的基本單位
• 一個stage會等待它包含的其他stage中的任務全部完成才加入調度
• 遵循數據局部性原則,使得數據傳輸代價最小
1. 如果一個任務需要的數據在某個節點的內存中,這個任務就會被分配至那個節點
2. 需要的數據在某個節點的文件系統中,就分配至那個節點
(此時的調度指的是:由spark的AM來決定計算partition的task,分配到哪個executor上)
• 如果此task失敗,AM會重新分配task
• 如果task依賴的上層partition數據已經失效了,會先將其依賴的partition計算任務再重算一遍
• 寬依賴中被依賴partition,可以將數據保存HDFS,以便快速重構(checkpoint)
注: 窄依賴只依賴上層一個partition,恢復代價較少;寬依賴依賴上層所有partition,如果數據丟
失,上層所有partiton都要重算
• 可以指定保存一個RDD的數據至節點的cache中,如果內存不夠,會LRU釋放一部
分,仍有重構的可能
Spark優化相關
內存管理
• Executor的內存分爲3塊
第一塊:讓task執行代碼,默認佔executor總內存的20%
第二塊:task通過shuffle過程拉取上一個stage的task的輸出後,進行聚合等操作時使用,默認也佔20%
第三塊:讓RDD持久化時使用,默認佔60%
• Task的執行速度和每個executor進程的CPU Core數量有直接關係,一個CPU Core同一
時間只能執行一個線程,每個executor進程上分配到的多個task,都是以task一條線程的
方式,多線程併發運行的。如果CPU Core數量比較充足,而且分配到的task數量比較合
理,那麼可以比較快速和高效地執行完這些task線程
重要的參數
• num-executors:該作業總共需要多少executor進程執行(建議50~100個左右合適)
• executor-memory:單個excutor的能使用的內存的大小,executor-memory *num-executors
就是本次任務需要的內存(儘量不要超過最大總內存的1/3~1/2 4G~8G較合適)
• executor-cores:單個executor 執行給的core的數量,不能超過單節點的core的總和;
單個core同一時間只能執行一個task,在不影響其他人作業,且不超過節點的core的上限的時候,這個值越大執行的效率越高;該參數決定每個
executor進程並行執行task線程的能力(不要超過總CPU Core的1/3~1/2 2~4個較合適)
開發原則
1、避免創建重複的RDD
2、儘可能複用同一個RDD
3、對多次使用的RDD進行持久化處理
藉助cache()和persist()方法
4、避免使用shuffle類算子
在spark作業運行過程中,最消耗性能的地方就是shuffle過程
將分佈在集羣中多個節點上的同一個key,拉取到同一個節點上,進行聚合和join處理,比如
groupByKey、reduceByKey、join等算子,都會觸發shuffle,map爲非shuffle算子
5、使用map-side預聚合的shuffle操作
6、使用Kryo優化序列化性能(不使用默認的Java序列化機制)
Spark技術棧
• Spark和Hadoop關係: Spark依賴於HDFS文件系統,如果是Spark on YARN部署模式,又依賴於YARN計算框架
• Spark Core:基於RDD提供操作接口,利用DAG進行統一的任務規劃
• Spark SQL:Hive的表 + Spark的裏。通過把Hive的HQL轉化爲Spark DAG計算來實現
• Spark Streaming:Spark的流式計算框架
• MLIB:Spark的機器學習庫,包含常用的機器學習算法
• GraphX:Spark圖並行操作庫