大數(shù)據(jù)計算引擎當中,Spark受到的重視是越來越多的,尤其是對數(shù)據(jù)處理實時性的要求越來越高,Hadoop原生的MapReduce引擎受到詬病,Spark的性能也需要不斷調(diào)整優(yōu)化。今天的大數(shù)據(jù)入門分享,我們就來講講SparkCore開發(fā)調(diào)優(yōu)原則。
Spark在大數(shù)據(jù)領(lǐng)域,能夠?qū)崿F(xiàn)離線批處理、SQL類處理、流式/實時計算、機器學(xué)習(xí)、圖計算等各種不同類型的計算操作,對于企業(yè)而言是低成本下的可靠性選擇,但是想要真正用好Spark,實現(xiàn)真正的高性能,調(diào)優(yōu)是不可或缺的手段。
SparkCore開發(fā)調(diào)優(yōu)原則
1、避免創(chuàng)建重復(fù)的RDD
通常來說,我們在開發(fā)一個Spark作業(yè)時,首先是基于某個數(shù)據(jù)源(比如Hive表或HDFS文件)創(chuàng)建一個初始的RDD;接著對這個RDD執(zhí)行某個算子操作,然后得到下一個RDD;以此類推,循環(huán)往復(fù),直到計算出最終我們需要的結(jié)果。
我們在開發(fā)過程中要注意:對于同一份數(shù)據(jù),只應(yīng)該創(chuàng)建一個RDD,不能創(chuàng)建多個RDD來代表同一份數(shù)據(jù)。否則,我們的Spark作業(yè)會進行多次重復(fù)計算來創(chuàng)建多個代表相同數(shù)據(jù)的RDD,進而增加作業(yè)的性能開銷。
2、盡可能復(fù)用同一個RDD
除了要避免在開發(fā)過程中對一份完全相同的數(shù)據(jù)創(chuàng)建多個RDD之外,在對不同的數(shù)據(jù)執(zhí)行算子操作時還要盡可能地復(fù)用一個RDD。
尤其對于類似這種多個RDD的數(shù)據(jù)有重疊或者包含的情況,盡量復(fù)用一個RDD,這樣可以盡可能地減少RDD的數(shù)量,從而盡可能減少算子執(zhí)行的次數(shù)。
3、對多次使用的RDD進行持久化
Spark中對于一個RDD執(zhí)行多次算子的默認原理是這樣的:每次你對一個RDD執(zhí)行一個算子操作時,都會重新從源頭處計算一遍,計算出那個RDD來,然后再對這個RDD執(zhí)行你的算子操作。這種方式的性能是很差的。
而對多次使用的RDD進行持久化,Spark就會根據(jù)你的持久化策略,將RDD中的數(shù)據(jù)保存到內(nèi)存或者磁盤中。以后每次對這個RDD進行算子操作時,都會直接從內(nèi)存或磁盤中提取持久化的RDD數(shù)據(jù),然后執(zhí)行算子,而不會從源頭處重新計算一遍這個RDD,再執(zhí)行算子操作。
4、盡量避免使用shuffle類算子
如果有可能的話,要盡量避免使用shuffle類算子。因為Spark作業(yè)運行過程中,最消耗性能的地方就是shuffle過程。shuffle過程,簡單來說,就是將分布在集群中多個節(jié)點上的同一個key,拉取到同一個節(jié)點上,進行聚合或join等操作。
比如reduceByKey、join等算子,都會觸發(fā)shuffle操作。沒有shuffle操作或者僅有較少shuffle操作的Spark作業(yè),可以大大減少性能開銷。
5、使用map-side預(yù)聚合的shuffle操作
如果因為業(yè)務(wù)需要,一定要使用shuffle操作,無法用map類的算子來替代,那么盡量使用可以map-side預(yù)聚合的算子。
所謂的map-side預(yù)聚合,說的是在每個節(jié)點本地對相同的key進行一次聚合操作,類似于MapReduce中的本地combiner。map-side預(yù)聚合之后,每個節(jié)點本地就只會有一條相同的key,因為多條相同的key都被聚合起來了。其他節(jié)點在拉取所有節(jié)點上的相同key時,就會大大減少需要拉取的數(shù)據(jù)數(shù)量,從而也就減少了磁盤IO以及網(wǎng)絡(luò)傳輸開銷。
6、使用高性能的算子
除了shuffle相關(guān)的算子有優(yōu)化原則之外,其他的算子也都有著相應(yīng)的優(yōu)化原則。
比如說使用reduceByKey/aggregateByKey替代groupByKey;使用mapPartitions替代普通map;使用foreachPartitions替代foreach;使用filter之后進行coalesce操作;使用repartitionAndSortWithinPartitions替代repartition與sort類操作等。
7、廣播大變量
在開發(fā)過程中,有時會遇到需要在算子函數(shù)中使用外部變量的場景(尤其是大變量,比如100M以上的大集合),那么此時就應(yīng)該使用Spark的廣播(Broadcast)功能來提升性能。
在算子函數(shù)中使用到外部變量時,默認情況下,Spark會將該變量復(fù)制多個副本,通過網(wǎng)絡(luò)傳輸?shù)絫ask中,此時每個task都有一個變量副本。如果變量本身比較大的話(比如100M,甚至1G),那么大量的變量副本在網(wǎng)絡(luò)中傳輸?shù)男阅荛_銷,以及在各個節(jié)點的Executor中占用過多內(nèi)存導(dǎo)致的頻繁GC,都會極大地影響性能。
8、使用Kryo優(yōu)化序列化性能
在Spark中,主要有三個地方涉及到了序列化:
在算子函數(shù)中使用到外部變量時,該變量會被序列化后進行網(wǎng)絡(luò)傳輸。
將自定義的類型作為RDD的泛型類型時(比如JavaRDD,Student是自定義類型),所有自定義類型對象,都會進行序列化。因此這種情況下,也要求自定義的類必須實現(xiàn)Serializable接口。
使用可序列化的持久化策略時(比如MEMORY_ONLY_SER),Spark會將RDD中的每個partition都序列化成一個大的字節(jié)數(shù)組。
對于這三種出現(xiàn)序列化的地方,我們都可以通過使用Kryo序列化類庫,來優(yōu)化序列化和反序列化的性能。
關(guān)于大數(shù)據(jù)入門,SparkCore開發(fā)調(diào)優(yōu)原則,以上就為大家做了簡單的介紹了。SparkCore作為Spark的核心部分,要真正掌握Spark框架,那么核心部分一定要吃透。