天天看點

基于Alluxio系統的Spark DataFrame高效存儲管理技術

介紹

越來越多的公司群組織開始将Alluxio和Spark一起部署進而簡化資料管理,提升資料通路性能。Qunar最近将Alluxio部署在他們的生産環境中,進而将Spark streaming作業的平均性能提升了15倍,峰值甚至達到300倍左右。在未使用Alluxio之前,他們發現生産環境中的一些Spark作業會變慢甚至無法完成。而在采用Alluxio後這些作業可以很快地完成。在這篇文章中,我們将介紹如何使用Alluxio幫助Spark變得更高效,具體地,我們将展示如何使用Alluxio高效存儲Spark DataFrame。

Alluxio和Spark緩存

使用者使用Alluxio存儲Spark DataFrame非常簡單:通過Spark DataFrame write API将DataFrame作為一個檔案寫入Alluxio。通常的做法是使用df.write.parquet()将DataFrame寫成parquet檔案。在DataFrame對應的parquet檔案被寫入Alluxio後,在Spark中可以使用sqlContext.read.parquet()讀取。為了分析了解使用Alluxio存儲DataFrame和使用Spark内置緩存存儲DataFrame在性能上差異,我們進行了如下的一些實驗。

實驗相關設定如下:

硬體配置:單個worker安裝在一個節點上,節點配置:61 GB記憶體 + 8核CPU;

軟體版本:Spark 2.0.0和Alluxio1.2.0,參數均為預設配置;

運作方式:以standalone模式運作Spark和Alluxio。

在本次實驗中,我們使用Spark内置的不同緩存級别存儲DataFrame對比測試使用Alluxio存儲DataFrame,然後收集分析性能測試結果。同時通過改變DataFrame的大小來展示存儲的DataFrame的規模對性能的影響。

存儲DataFrame

Spark DataFrame可以使用persist() API存儲到Spark緩存中。persist()可以緩存DataFrame資料到不同的存儲媒介。

本次實驗使用了以下Spark緩存存儲級别(StorageLevel):

MEMORY_ONLY:在Spark JVM記憶體中存儲DataFrame對象

MEMORY_ONLY_SER:在Spark JVM記憶體中存儲序列化後的DataFrame對象

DISK_ONLY: 将DataFrame資料存儲在本地磁盤

下面是一個如何使用persist() API緩存DataFrame的例子:

df.persist(MEMORY_ONLY)           

将DataFrame儲存在記憶體中的另一種方法是将DataFrame作為一個檔案寫入Alluxio。Spark支援将DataFrame寫成多種不同的檔案格式,在本次實驗中,我們将DataFrame寫成parquet檔案。

下面是一個将DataFrame寫入Alluxio的例子:

查詢存儲在Alluxio上的DataFrame

DataFrame被儲存後(無論存儲在Spark記憶體還是Alluxio中),應用可以讀取DataFrame以進行後續的計算任務。本次實驗中,我們建立了一個包含2列的DataFrame(這2列的資料類型均為浮點型),計算任務則是分别計算這2列資料之和。

當DataFrame存儲在Alluxio時,Spark讀取DataFrame就像從Alluxio中讀取檔案一樣簡單。下面是一個從Alluxio中讀取DataFrame的例子:

df = sqlContext.read.parquet(alluxioFile)
df.agg(sum("s1"), sum("s2")).show()           

我們分别從Alluxio中 parquet檔案以及各種Spark存儲級别緩存中讀取DataFrame,并進行上述的聚合計算操作。下圖顯示了不同存儲方案中的聚合操作的完成時間。

基于Alluxio系統的Spark DataFrame高效存儲管理技術

從上圖可以看出,從Alluxio中讀取DataFrame進行聚合操作具有比較穩定的執行性能。對于從Spark緩存中讀取DataFrame,在DataFrame規模較小時執行性能具有一定優勢,但是随着DataFrame規模的增長,性能急劇下降。在本文的實驗環境中,對于各種Spark内置的存儲級别, DataFrame規模達到20 GB以後,聚合操作的性能下降比較明顯。

另一方面,相比使用Spark内置緩存,使用Alluxio存儲DataFrame并進行聚合操作,其性能在小規模資料上略有劣勢。然而,随着DataFrame資料規模的增長,從Alluxio中讀取DataFrame性能更好,因為從Alluxio中讀取DataFrame的耗時幾乎始終随着資料規模線性增長。由于使用Alluxio存儲DataFrame的讀寫性能具有較好的線性可擴充性,上層應用可以穩定地以記憶體速度處理更大規模的資料。

使用Alluxio共享存儲的DataFrame

使用Alluxio存儲DataFrame的另一大優勢是可以在不同Spark應用或作業之間共享存儲在Alluxio中的資料。當一個DataFrame檔案被寫入Alluxio後,它可以被不同的作業、SparkContext、甚至不同的計算架構共享。是以,如果一個存儲在Alluxio中的DataFrame被多個應用頻繁地通路,那麼所有的應用均可以從Alluxio記憶體中直接讀取資料,并不需要重新計算或者從另外的底層外部資料源中讀取資料。

為了驗證采用Alluxio共享記憶體的優勢,我們在如上述的同樣的實驗環境中進行相同規模的DataFrame聚合操作。當使用50 GB規模的DataFrame時,我們在單個Spark應用中進行聚合操作,并且記錄該聚合操作的耗時。沒有使用Alluxio時,Spark應用需要每次都從資料源讀取資料(在本次實驗中是一個本地SSD)。在使用Alluxio時,資料可以直接從Alluxio記憶體中讀取。下圖顯示了2次聚合操作的完成時間性能對比。使用Alluxio的情況下,聚合操作快了約2.5倍。

基于Alluxio系統的Spark DataFrame高效存儲管理技術

在上圖的實驗中,資料源是本地SSD。如果DataFrame來自通路起來更慢或不穩定的資料源,Alluxio的優勢就更加明顯了。舉例而言,下圖是DataFrame資料源由本地SSD替換為某公有雲存儲的實驗結果。

基于Alluxio系統的Spark DataFrame高效存儲管理技術

這張圖顯示是執行7次聚合操作的平均完成時間。圖中的紅色的誤差範圍(error bar)代表完成時間的最大和最小範圍。這些結果清晰地顯示出Alluxio可以顯著提升操作的平均性能。這是因為使用Alluxio緩存DataFrame時,Spark可以直接從Alluxio記憶體中讀取DataFrame,而不是從遠端的公有雲存儲中。平均而言,Alluxio可以加速上述DataFrame的聚集操作性能超過10倍。

另一方面,由于資料源是公有雲系統,Spark必須跨網絡遠端讀取資料。錯綜複雜的網絡狀況會導緻讀取性能難以預測。這種性能的不穩定性從上圖中的誤差範圍(error bar)可以很明顯地看出。在不使用Alluxio的情況下,Spark作業的完成時間變化範圍超過1100秒。當使用Alluxio之後,完成時間的變化範圍隻有10秒。在本實驗中,Alluxio能夠将資料讀取造成的不穩定性降低超過100倍。

由于共有雲存儲系統的網絡通路性能不可預測性,最慢的Spark作業執行時間超過1700秒, 比平均慢2倍。然而,當使用Alluxio時,最慢的Spark作業執行時間大約比平均時間隻慢6秒。是以,如果以最慢的Spark作業執行時間來評估,Alluxio可以加速DataFrame聚合操作超過17倍。

結論

Alluxio可以在多個方面幫助Spark變得更高效。這篇文章介紹了如何使用Alluxio存儲Spark DataFrame,并且實驗驗證了采用Alluxio帶來的優勢:

Alluxio可以直接在記憶體中儲存大規模的資料來加速Spark應用;

Alluxio能夠在多個Spark應用之間快速共享存儲在記憶體中的資料;

Alluxio可以提供穩定和可預測的資料通路性能。

轉載自:

https://mp.weixin.qq.com/s/c7mq46uyudcdqzAut1pqeA

繼續閱讀