工業和研究中資料的大幅增長為電腦科學帶來了巨大的機會與挑戰。由於資料大小超過了單臺機器的能力,使用者需要新的系統將計算擴充套件到多個節點。因此,針對不同計算工作負載的新叢集程式設計模型已呈爆炸式增長。
這些模型相對專業化。例如支援批處理的MapReduce,支援迭代圖演算法的Dreme。在開源Apache Hadoop堆疊中,類似Storm和Impala的系統也是特有的。即使在關聯式資料庫世界中,“一刀切”系統已越來越少。然而,很多大資料應用需要整合許多不同的處理型別。大資料,顧名思義,代表了資料的多樣性和複雜性。一個典型的管道需要類似MapReduce的系統進行資料載入,使用類似SQL的語言進行查詢。使用者不得不將不同的系統整合在一起,並且有時候引擎也不能對應用的需求都滿足。
有鑑於此,2009年加州大學伯克利分校團隊開始了Apache Spark專案,旨在為分散式資料處理設計一個統一的引擎。 Spark具有類似於MapReduce的程式設計模型,但是使用稱為“彈性分散式資料集”或RDDs的資料共享抽象擴充套件。通過這個簡單的擴充套件,Spark可以輕鬆應對之前需要單獨引擎處理的高強度工作,包括SQL、流式傳輸、機器學習和圖形處理。Spark使用與專用引擎相同的優化(例如面向列的處理和增量更新),並實現相同的效能,但是編寫更為高效。
Spark的通用性有幾個重要的好處。
首先,應用程式更容易開發,因為它們使用統一的API。
第二,結合處理任務更有效;而先前的系統需要將資料寫入儲存以將其傳遞給另一個引擎,Spark可以在相同的資料(通常在儲存器中)上執行不同的功能。
最後,Spark啟用了以前系統無法實現的新應用程式(如圖形上的互動式查詢和流式計算機學習)。自2010年釋出以來,Spark已經發展成為最活躍的開源專案或大資料處理,擁有超過1,000名貢獻者。該專案已在超過1,000個組織中使用,從技術公司到銀行、零售、生物技術和天文學。
隨著並行資料處理變得普遍,處理功能的可組合性將是對可用性和效能的最重要關注之一。許多資料分析是探索性的,使用者希望將庫函式快速組合成一個工作管道。然而,對於“大資料”,特別是在不同系統之間複製資料是對效能不利的。因此,使用者需要共性和可組合的抽象。在本文中,我們將介紹Spark程式設計模型並解釋為什麼它是高度通用的。我們還討論瞭如何利用這種通用性來構建其它處理任務。最後,我們總結了Spark中常見的應用程式。
程式設計模型
Spark中的關鍵程式設計抽象是RDD,它是容錯集合,可以並行處理叢集中的物件。使用者通過“轉換”(例如map、filter和groupBy)操作來建立RDD。
Spark通過Scala、Java、Python和R中的函數語言程式設計API來表達RDD,使用者可以簡單地在叢集上執行本地函式。 例如,以下Scala程式碼通過搜尋以ERROR開頭的行來建立日誌檔案中錯誤訊息的RDD,然後列印總錯誤數:
1 2 3 4 5 |
lines = spark.textFile("hdfs://...") errors = lines.filter(s => s.startsWith("ERROR")) println("Total errors: "+errors.count()) |
第一行定義了一個在HDFS上的文字行集合RDD。第二行呼叫過濾器轉換以從行中匯出新的RDD。它的引數是一個Scala函式文字或閉包。最後一行呼叫count函式。另一種型別的RDD操作稱為“動作”,返回一個結果給程式(這裡,RDD中的元素數量),而不是定義一個新的RDD。
Spark評估RDDs延遲,嘗試為使用者運算找到一個有效的計劃。特別的是,變換返回表示計算結果的新RDD物件,但不立即計算它。當一個動作被呼叫時,Spark檢視整個用於建立執行計劃的轉換的圖。例如,如果一行中有多個過濾器或對映操作,Spark可以將它們融合到一個傳遞中,或者如果知道資料是被分割槽的,它可以避免通過網路為groupBy進行資料傳遞。因此使用者可以實現程式模組化,而不會造成效能低下。
最後,RDDs為計算之間的資料共享提供了明確的支援。預設情況下,RDD是“短暫的”,因為它們每次在動作(例如count)使用時被重新計算。但是,使用者還可以將所選的RDD保留在記憶體中或快速重用。(如果資料不適合記憶體,Spark還會將其溢位到磁碟。)例如,使用者在HDFS中搜尋大量日誌資料集來進行錯誤除錯時,可以通過呼叫以下函式來載入不同叢集的錯誤資訊到記憶體中:
1 |
errors.persist() |
隨後,使用者可以在該記憶體中資料上執行不同的查詢:
1 2 3 4 5 6 7 8 9 |
// Count errors mentioning MySQL errors.filter(s => s.contains("MySQL")).count() // Fetch back the time fields of errors that // mention PHP, assuming time is field #3: errors.filter(s => s.contains("PHP")).map(line => line.split('t')(3)).collect() |
這種資料共享是Spark和以前的計算模型(如MapReduce)之間的主要區別。
容錯
除了提供資料共享和各種並行操作,RDDs還可以自動從故障中恢復。 傳統上,分散式計算系統通過資料複製或檢查點提供了容錯。 Spark使用一種稱為“lineage”的新方法。每個RDD跟蹤用於構建它的轉換圖,並對基本資料重新執行這些操作,以重建任何丟失的分割槽。
例如,圖2顯示了我們以前的查詢中的RDD,其中我們通過應用兩個過濾器和一個對映來獲取錯誤的時間欄位。 如果RDD的任何分割槽丟失(例如儲存記憶體分割槽的錯誤的節點失敗),Spark將通過在HDFS檔案的相應塊上的應用過濾器來重建它。 對於將資料從所有節點傳送到所有其他節點(例如reduceByKey)的“shuffle”操作,傳送方在本地保留其輸出資料,以防接收器出現錯誤。
基於沿襲的恢復比資料密集型工作負載中的複製效率高得多。 它節省了時間,因為寫入RAM要比通過網路寫入資料快。 恢復通常比簡單地重新執行程式快得多,因為故障節點通常包含多個RDD分割槽,這些分割槽可以在其他節點上並行重建。
另外一個複雜些的例子如圖3:
Spark中邏輯迴歸的實現。 它使用批量梯度下降,一個簡單的迭代演算法,重複計算資料上的梯度函式作為並行求和。 Spark可以方便地將資料載入到RAM中,並執行多個求和。 因此,它執行速度比傳統的MapReduce快。 例如,在100GB作業中(如圖4),MapReduce每次迭代需要110秒,因為每次迭代需從磁碟載入資料,而Spark在第一次載入後每次迭代只需要一秒。
與儲存系統的整合
與Google的MapReduce非常相似,Spark旨在與多個外部系統一起使用持久儲存。Spark最常用於叢集檔案系統,如HDFS和鍵值儲存,如S3和Cassandra。 它還可以作為資料目錄與Apache Hive連線。 RDD通常僅在應用程式中儲存臨時資料,但某些應用程式(例如Spark SQL JDBC伺服器)也在多個使用者之間共享RDD。Spark作為儲存系統無關引擎的設計,使使用者可以輕鬆地對現有資料進行運算和連線各種資料來源。
高階庫
RDD程式設計模型僅提供物件的分散式集合和在其上執行的函式。除此之外,我們在Spark上構建了各種針對專用計算引擎更高階的庫。其關鍵思想是,如果我們控制儲存在RDD中的資料結構,跨節點的資料分割槽以及在其上執行的函式,我們可以在其他引擎中實現許多執行技術。事實上,正如我們在本節中所展示的,這些庫通常在每個任務上實現最先進的效能,同時在使用者組合使用它們時提供顯著的優勢。我們現在討論Apache Spark包含的四個主要庫。
SQL和DataFrames。最常見的資料處理範例之一是關係查詢。 Spark SQL及其前身Shark使用類似於分析資料庫的技術在Spark上實現這樣的查詢。例如,這些系統支援列式儲存,基於成本的優化和用於查詢執行的程式碼生成。這些系統的主要思想是使用與分析資料庫相同的資料佈局 – 壓縮的柱狀儲存 – 內部RDD。在Spark SQL中,RDD中的每個記錄都儲存為以二進位制格式儲存的一系列行,並且系統生成直接針對此佈局執行的程式碼。
除了執行SQL查詢之外,我們還使用Spark SQL引擎為稱為DataFrames的基本資料變換提供了更高階的抽象,這些變換是具有已知模式的記錄的RDD。 DataFrames是R和Python中的表格資料的常見抽象,具有用於過濾,計算新列和聚合的程式設計方法。在Spark中,這些操作對映到Spark SQL引擎並接收其所有優化。我們稍後討論DataFrames。
Spark SQL中尚未實現的一種技術是索引,儘管Spark上的其他庫(如IndexedRDDs)確實使用它。
Spark Streaming(流)。 Spark Streaming使用稱為“離散流”的模型實現增量流處理。為了通過Spark實現流式傳輸,我們將輸入資料分成小批量(例如每200毫秒),我們定期與RDD中儲存的狀態組合以產生新結果。以這種方式執行流計算比傳統的分散式流系統有幾個好處。例如,由於使用沿襲,故障恢復更便宜,並且可以將流與批處理和互動式查詢組合。
GraphX。 GraphX提供了類似於Pregel和GraphLab的圖形計算介面,1通過為其構建的RDD選擇分割槽函式來實現與這些系統相同的佈局優化(例如頂點分割槽方案)。
MLlib。 MLlib,Spark的機器學習庫,實現了50多種常見的分散式模型訓練演算法。例如,它包括決策樹(PLANET),Latent Dirichlet分佈和交替最小二乘矩陣分解的常見分散式演算法。
組合處理任務。 Spark的庫都對RDD進行操作,作為資料抽象,使得它們在應用程式中易於組合。例如,圖5顯示了一個程式,它使用Spark SQL讀取一些歷史Twitter資料,使用MLlib訓練一個K-means聚類模型,然後將該模型應用於一個新的tweet流。每個庫返回的資料任務(這裡是歷史性的tweet RDD和K-means模型)很容易傳遞給其他庫。
除了API級別的相容性,Spark中的組合在執行級別也是高效的,因為Spark可以跨處理庫進行優化。例如,如果一個庫執行對映函式,並且下一個庫對其結果執行對映,則Spark將這些操作融合到單個對映中。同樣,Spark的故障恢復在這些庫中無縫地工作,重新計算丟失的資料,無論哪個庫產生它。
效能
假設這些庫執行在同一引擎上,它們是否會失去效能?我們發現,通過實現我們剛剛在RDD中概述的優化,我們通常可以匹配專用引擎的效能。例如,圖6比較了Spark對三個簡單任務(SQL查詢,流字計數和交替最小二乘矩陣分解)與其他引擎的效能。雖然結果隨著工作負載的不同而不同,但Spark通常與Storm,GraphLab和Impala等專用系統相當。對於流處理,雖然我們顯示了Storm上分散式實現的結果,但是每個節點的吞吐量也可以與商業流引擎如Oracle CEP相媲美。
即使在高度競爭的基準測試中,我們也使用Apache Spark實現了最先進的效能。在2014年,我們進入了Daytona Gray-Sort基準(http://sortbenchmark.org/),涉及在磁碟上排序100TB的資料,並繫結一個專門的系統構建的新記錄,僅用於在類似數量的機器上排序。與其他示例一樣,這是可能的,因為我們可以實現RDD模型中大規模排序所需的通訊和CPU優化。
應用
Apache Spark用於廣泛的應用程式。我們對Spark使用者的調查發現了超過1,000家使用Spark的公司,從Web服務,生物技術到金融等領域。在學術界,我們也看到了幾個科學領域的應用。在這些工作負載中,我們發現使用者利用Spark的通用性,並且通常組合其多個庫。在這裡,我們介紹幾個頂級用例。許多用例的簡報也可在Spark Summit會議網站(http://www.spark-summit.org)上獲取。
批量處理
Spark最常用的應用程式是對大型資料集進行批處理,包括Extract-Transform-Load工作負載,將資料從原始格式(如日誌檔案)轉換為更加結構化的格式,並離線訓練機器學習模型。這些工作負載的已釋出示例包括Yahoo!的頁面個性化和推薦;管理高盛的資料湖;阿里巴巴圖表挖掘;金融價值風險計算;和豐田的客戶反饋的文字挖掘。我們知道的最大的已釋出的用例是在中國社交網路騰訊的8000節點叢集,每天攝取1PB的資料。
雖然Spark可以在記憶體中處理資料,但是此類別中的許多應用程式只能在磁碟上執行。在這種情況下,Spark相對於MapReduce仍然可以提高效能,因為它支援更復雜的運算子圖。
互動式查詢
互動使用Spark分為三個主要類別。首先,組織通常通過商業智慧工具(如Tableau)使用Spark SQL進行關係查詢。例子包括eBay和百度。第二,開發人員和資料科學家可以通過shell或視覺化筆記本環境以互動方式使用Spark的Scala,Python和R介面。這種互動式使用對於提出更高階的問題和設計最終導致生產應用程式的模型至關重要,並且在所有部署中都很常見。第三,一些供應商已經開發了在Spark上執行的特定領域的互動式應用程式。示例包括Tresata(反洗錢),Trifacta(資料清理)和PanTera(大規模視覺化,如圖7所示)。
流處理
實時處理也是一種流行的用例,無論是在分析和實時決策應用程式中。 Spark Streaming的已釋出使用案例包括思科的網路安全監控,三星SDS的規範分析以及Netflix的日誌挖掘。許多這些應用程式還將流式處理與批處理和互動式查詢相結合。例如,視訊公司Conviva使用Spark持續維護內容分發伺服器效能的模型,在跨伺服器移動客戶端時自動查詢,在需要對模型維護和查詢進行大量並行工作的應用程式中。
科學應用
Spark還被用於幾個科學領域,包括大規模垃圾郵件檢測,影象處理,和基因組資料處理。結合批量,互動和流處理的一個例子是Howard Hughes醫學院的Thunder平臺神經科學,Janelia Farm。它被設計成處理來自實驗的腦成像資料,實時地,從生物體(例如斑馬魚和小鼠)擴大到1TB /小時的全腦成像資料。使用Thunder,研究人員可以應用機器學習演算法(例如聚類和主成分分析)來識別涉及特定行為的神經元。相同的程式碼可以在批處理作業中對來自先前執行的資料或在活動實驗期間的互動式查詢中執行。圖8顯示了使用Spark生成的示例影象。
使用的Spark元件
因為Spark是一個統一的資料處理引擎,自然的問題是它的圖書館組織實際使用了多少。我們對Spark使用者的調查表明,組織確實使用多個元件,超過60%的組織使用至少三個Spark的API。圖9概述了Databricks 2015年7月Spark調查中每個元件的使用情況,達到1400名受訪者。我們將Spark Core API(只是RDD)列為一個元件,將更高階別的庫列為其他元件。
我們看到許多元件被廣泛使用,Spark Core和SQL最受歡迎。 Streaming在46%的組織中使用,機器學習在54%中使用。雖然在圖9中未直接示出,但大多陣列織使用多個元件; 88%使用其中至少兩個,60%使用至少三個(如Spark Core和兩個庫),27%使用至少四個元件。
部署環境
我們也看到Apache Spark應用程式執行的地方和它們連線到的資料來源的多樣性。雖然第一個Spark部署通常在Hadoop環境中,在2015年7月Spark調查中,僅有40%的部署在Hadoop YARN叢集管理器上。此外,52%的受訪者在公共雲上執行Spark。
Spark模型的魅力
雖然Apache Spark演示了統一的叢集程式設計模型是可行和有用的,但是瞭解叢集程式設計模型的廣泛性成因以及Spark的侷限性是很有好處的。在這裡,我們總結了一個關於Zaharia RDDs的一般性的討論。我們從兩個角度研究RDDs。首先,從能力的角度,我們認為RDDs可以模擬任何分散式計算,並且在多數情況下表現優異,除非計算對網路延遲敏感。第二,從系統的角度來看,RDD能幫助應用程式對叢集中最常見瓶頸的資源進行控制 – 網路和儲存I/O,從而使得這些資源得到優化。
表達性角度。為了研究RDDs的表達性,我們首先比較RDDs和MapReduce模型。第一個問題是MapReduce本身表達性的計算是什麼?雖然有關於MapReduce的限制的許多討論,這裡令人驚訝的是MapReduce可以模擬任何分散式計算。
要看到這一點,請注意任何分散式計算由執行本地計算和偶爾交換訊息的節點組成。 MapReduce提供了對映操作,允許本地計算和reduce,這允許全部通訊。因此,可以通過將其工作分解為時間步長,執行Map以在每個時間步長中執行本地計算,以及在每個步驟結束時使用reduce來批處理和交換訊息,來模擬任何分散式計算,儘管效率並不高。一系列MapReduce步驟將捕獲整個結果,如圖10所示。
雖然這一行的工作表明MapReduce可以模擬任意計算,但又兩個問題會使這種模擬背後的“常數因子”高。首先,MapReduce在跨時間段共享資料方面效率低下,因為它依賴於複製的外部儲存系統來實現此目的。由於需在每個步驟之後寫出其狀態,系統執行將較慢。其次,MapReduce步驟的延遲決定了我們的模擬與真實網路的匹配程度,大多數Map-Reduce實現是針對具有幾分鐘到幾小時延遲的批處理環境設計的。
RDDs和Spark解決了這兩個問題。在資料共享方面,RDD通過避免中間資料的複製來快速進行資料共享,並且可以緊密模擬在由長時間執行的程式組成的系統中發生的記憶體中“資料共享”。在延遲方面,Spark可以在大型叢集上以100ms延遲執行MapReduce類似的步驟。雖然一些應用程式需要更細粒度的時間步長和通訊,但是這100ms的延遲足以處理許多資料密集型工作負載,在通訊步驟之前可以大批量進行計算。
總之,RDDs建立在Map-Reduce模擬任何分散式計算的能力之上,但更有效率。它們的主要限制是由於每個通訊步驟中的同步而增加的等待時間,但是該等待時間的損失與所得相比是可以忽略的。
系統觀點。獨立於表徵Spark的通用性的模擬方法,我們可以採用系統方法。叢集計算中的瓶頸資源是什麼? RDD可以有效地使用它們嗎?雖然叢集應用程式是多樣的,但它們都受底層硬體的相同屬性的約束。當前資料中心具有陡峭的儲存層次結構,以相似的方式限制大多數應用。例如,典型的Hadoop叢集可能具有以下特性:
本地儲存。每個節點具有本地儲存器,大約50GB/s的頻寬,以及10到20個本地磁碟,大約1GB/s到2GB/ s的磁碟頻寬。
連結。每個節點具有10Gbps(1.3GB/s)鏈路,或者比其儲存器頻寬小約40x,並且比其總的磁碟頻寬小2倍。
機架。節點被組織成20到40臺機器的機架,每個機架的頻寬為40Gbps-80Gbps,或者機架內網路效能的2-5倍。
給定這些屬性,在許多應用中最重要的效能問題是在網路中放置資料和計算。幸運的是,RDD提供了控制這種放置的設施;該介面允許應用程式在輸入資料附近放置計算(通過用於輸入源25的“優選位置”的API),並且RDD提供對資料分割槽和共置(例如指定資料被給定金鑰雜湊)的控制。因此,庫(例如GraphX)可以實現在專門系統中使用的相同的佈置策略。
除了網路和I / O頻寬,最常見的瓶頸往往是CPU時間,特別是如果資料在記憶體中。在這種情況下,Spark可以執行在每個節點上的專用系統中使用的相同的演算法和庫。例如,它使用Spark SQL中的列儲存和處理,MLlib中的本機BLAS庫等。正如我們之前討論的,RDD明顯增加成本的唯一區域是網路延遲。
從系統角度來看的最後一個觀點是,由於容錯,Spark可能會對當今某些專用系統產生額外的成本。例如,在Spark中,每個shuffle操作中的map任務將它們的輸出儲存到它們執行的機器上的本地檔案,因此reduce任務可以稍後重新獲取。此外,Spark在shuffle階段實現了一個障礙,所以reduce任務不會開始,直到所有的Map已經完成。這避免了故障恢復所需的一些複雜性,如果一個“推”直接從對映記錄以流水線方式減少。雖然刪除一些這些功能將加快系統。但預設情況下,我們在Spark中會保持開啟容錯,以便於對應用程式進行容錯處理。
不斷探索
Apache Spark仍然是一個快速發展的專案。自2013年6月以來,程式碼庫規模增長了6倍。擁有超過200個第三方可用軟體包。在研究社群,Berkeley,MIT和Stanford的多個專案基於Spark,許多新的庫(如GraphX和Spark Streaming)來自研究小組。在這裡,我們簡述四個主要的成果。
DataFrames和更多的宣告性API。核心Spark API基於對包含任意型別的Scala,Java或Python物件的分散式集合的函數語言程式設計。雖然這種方法非常具有表現力,但也使程式更難以自動分析和優化。儲存在RDD中的Scala/ Java/Python物件可能具有複雜的結構,執行它們的函式可能包括任意程式碼。在許多應用程式中,如果開發人員沒有使用正確的運算子,他們可能會得到不理想的效能;例如,系統本身不能在Map之前推送過濾器功能。
為了解決這個問題,我們在2015年擴充套件了Spark,以便根據關係代數新增一個名為DataFrames的更具宣告性的API。資料幀是Python和R中表格資料的通用API。資料幀是一組具有已知模式的記錄,基本上等同於資料庫表,支援使用受限“表示式”API進行過濾和聚合等操作。然而,與在SQL語言中工作不同,資料幀操作被呼叫作為更通用的程式語言(例如Python和R)中的函式呼叫,允許開發人員使用主語言中的抽象(例如函式和類)。圖11和圖12顯示了API的示例。
Spark的DataFrames提供了類似於單節點程式包的API,但是使用Spark SQL的查詢計劃程式自動並行化和優化計算。使用者程式碼因此接收在Spark的功能API下不可用的優化(例如謂詞下推,運算子重新排序和連線演算法選擇)。據我們所知,Spark DataFrames是第一個在資料框架API.d下執行這種關係優化的庫。
雖然DataFrames仍然是新的,但是這不妨礙它的流行。在我們2015年7月的調查中,60%的受訪者報告使用它們。由於DataFrames的成功,我們還開發了一個名為Datasetse的型別安全介面,讓Java和Scala程式設計師將DataFrames視為Java物件的靜態型別集合,類似於RDD API,並仍然接收關係優化。我們期望這些API逐漸成為在Spark庫之間傳遞資料的標準抽象。
效能優化。Spark最近的許多工作都是在效能上。在2014年,Databricks團隊花費了大量的精力來優化Spark的網路和I/O元操作,在 Daytona GraySort挑戰中成功打破賽事記錄。挑戰是專案是對100TB資料進行排序,Spark的成績較前冠軍快了3倍,但是僅需1/10的裝置。這個基準測試不是在記憶體中執行,而是在(固態)磁碟上執行的。
R語言支援。 SparkR project在2015年被合併到Spark中,在R中提供了一個程式設計介面。R介面基於DataFrames,使用與R的內建資料框架幾乎完全相同的語法。其他Spark庫(如MLlib)也很容易從R中呼叫,因為它們接受DataFrames的輸入。
庫的研究。 Apache Spark繼續努力於構建更高階別的資料處理庫。最近的專案包括Thunder神經科學,ADAM基因組學以及Kira天文學影象處理。其他研究庫(如GraphX)已被合併到主要程式碼庫。
小結
可擴充套件資料處理對於下一代計算機應用是必不可少的,但通常涉及不同的計算系統。為了簡化這個任務,Spark專案為大資料應用程式引入了統一的程式設計模型和引擎。實踐證明,這樣的模型可以有效地支援當前的工作負荷,併為使用者帶來實質性的好處。希望Apache Spark能增強在大資料程式設計庫中的可組合性,並開發更易於使用者使用的庫。
本文中描述的所有Apache Spark庫都是開源的,可通過http://spark.apache.org/ 檢視。 Databricks還製作了所有Spark峰會的視訊,可在https://spark-summit.org/ 中免費獲得。