Apache Spark:如何選擇正確的數據抽象?

Apache Spark提供了三種不同的API來處理數據集:RDD,DataFrame和Dataset。 選擇正確的數據抽象是加快Spark作業執行速度並利用Spark內部優化的基礎。 此外,選擇一個好的數據結構可以加快開發過程。

Apache Spark:如何選擇正確的數據抽象?

> Photo by Tianyi Ma on Unsplash

介紹

如今,用於大數據處理的最廣泛的庫之一是Apache Spark,這是一種快速且通用的集群計算系統。 在map-reduce範式的框架中,Spark已成為事實上的標準。 實際上,map-reduce算法的第一個實現是由Google開發的; 但是,幾年之後,在Hadoop中發佈了一個開源實現。 最初,Hadoop發佈了一個低效率的版本,然後是一個優化的版本,它引入了使用YARN進行資源協商的功能。 然而,由於磁盤上的讀/寫操作導致Hadoop在IO方面表現不佳。 最終,Spark通過從內存而不是磁盤讀取數據帶來了令人印象深刻的優化,從而進入了場景。 這項創新極大地提高了程序的執行速度。

Apache Spark:如何選擇正確的數據抽象?

> Figure 1: components of a distributed Spark application.

簡而言之,每個Spark應用程序都包含一個驅動程序,並且許多執行程序分佈在群集節點上,它們執行並行操作並將結果返回給驅動程序(應用程序的主要部分)(參見圖1)。 為了執行並行和分佈式計算,Spark提供了用於處理數據束的不同結構:RDD,DataFrame和Dataset。

這三種數據抽象為用戶提供了相同的結果,但是它們在提供的API,性能和工作方式方面有所不同。

讓我們跳入他們!

RDD

自版本1.0起,Spark庫提供的主要數據抽象是RDD,它表示彈性分佈式數據集。 RDD是跨群集節點劃分的數據元素的容錯集合,可以使用Spark的API進行並行操作。 在大多數情況下,RDD是通過從分佈式數據存儲(例如HDFS,HBase,Cassandra或Hadoop支持的任何其他數據源)中加載數據,並行化Scala集合(例如List或Sequence)來創建的,或者 通過讀取存儲在本地文件系統中的文件。

從高級的角度來看,RDD可以被視為一組Java對象,它支持兩種類型的操作:轉換和操作。 轉換從現有的RDD創建一個新的RDD,而動作在對初始RDD進行計算後將值返回給驅動程序。 Spark範式的核心是懶惰的概念:僅當調用一個動作(即要求將結果返回給驅動程序時才有效地計算轉換)(參見圖2)。

Apache Spark:如何選擇正確的數據抽象?

> Figure 2: RDD operations.

假設您有一個包含莎士比亞的《羅密歐》和《朱麗葉》的文本文件,其中每行代表原始悲劇的一行。 您要計算單詞" Juliet"在全文中出現的次數。 為簡單起見,請考慮使用沒有標點符號的文本。此解析任務非常適合RDD。 基本上,您必須遵循以下步驟:

· 將文本文件加載到RDD中,其中每個RDD元素都是一行文本;

· 將所有行映射為小寫字母;

· 通過將空白字符上的所有行分開,將RDD壓縮為單詞的RDD(字符串類型);

· 過濾(保留)所有等於" juliet"的詞;

· 將所有元素映射到數字1;

· 應用帶有求和函數的reduce來獲得總結果。

RDD的一大優點是它們是編譯時類型安全的。 由於Scala是一種強類型語言,因此Scala編譯器能夠在編譯時檢測到任何類型的不兼容性,從而使您有機會修復任何錯誤而不必運行整個應用程序。

但是,由於RDD包含Java對象,因此它們同時遭受垃圾回收和Java序列化問題的困擾,這在數據增長時是昂貴的操作。 不幸的是,Spark沒有提供任何內置優化來加速這種過程。 因此,在庫中引入了DataFrames。

在繼續之前,請簡要回顧一下RDD的優缺點:

✔️面向對象和函數式編程風格

✔️對數據的低級控制

✔️編譯時類型安全

❌序列化和垃圾收集器問題

❌沒有內置優化

作為Quantyca的數據架構師和軟件開發人員,我每天必須做出選擇,以便快速交付高質量的代碼以及優化的算法。 因此,我決定寫這篇文章來簡要詳細介紹Apache Spark(著名的數據處理工具)提供的每種數據結構。 我將強調它們的優缺點,為您提供一些有關如何選擇正確的數據抽象的提示:

· RDD:Java對象的分佈式集合;

· DataFrame:結構化和無類型的數據集;

· 數據集:結構化但類型化的數據集,是RDD與DataFrame之間的完美結合。

在整個論文中,我將提供一些用Scala語言編寫的示例。

數據框 Dataframe

DataFrames包含在Spark SQL上下文中的Spark版本1.3中。 Spark SQL是用於結構化數據處理的Spark模塊,該模塊允許使用SQL查詢。

DataFrame是組織為命名列的數據集。 從概念上講,它等效於關係數據庫中的表或Python中的Pandas數據框。 更深入地講,在Scala中,DataFrame是Row對象的結構化數據集。 行是通用的無類型Java對象。 可以從不同的來源構造DataFrame:結構化的數據文件,Hive表,來自外部數據庫的表,甚至是現有的RDD。

與RDD相比,DataFrame提供更高級別的抽象。 實際上,DataFrames可以視為表。 Spark SQL提供API,以簡單的類似於SQL的語法在DataFrame上運行SQL查詢。

例如,假設您有一個稱為訂單的Hive表,其中包含有關電子商務訂單交付的信息。 為簡單起見,我們僅考慮表格的兩列:狀態和國家/地區。 您要計算每個國家的未結訂單。 使用類似SQL的語法,完成這樣的任務非常簡單(請參見代碼段中的版本1):

· 將訂單表加載到Spark DataFrame中;

· 使用where子句過濾(保留)已關閉的訂單;

· 調用一個組,後跟一個計數以得到結果。

或者,在加載表時,可以使用SQL查詢一次提取相同的結果(請參見代碼段中的版本2)。

與RDD不同,DataFrame在後臺進行了一些優化。 第一個是Catalyst查詢優化器,該引擎可解釋Spark代碼並構建優化的邏輯和物理查詢計劃。 第二個是鎢優化器,它實現了堆外存儲機制。 Tungsten優化器將序列化提供給堆外存儲,以便直接在此堆外存儲器上執行轉換,從而避免了與標準Java或Kryo序列化程序相關的序列化成本。 Tungsten還提供了整個階段的代碼生成器,該組件將優化的物理計劃轉換為Java字節碼,以在每個執行程序上運行。

這些優化在CPU和內存效率方面極大地加快了Spark作業的執行時間,從而大大減少了垃圾收集器的處理策略。

但是,所有閃閃發光的不是黃金。 不幸的是,DataFrames不是類型安全的:僅在運行時檢查類型。 例如,如果使用DataFrames編寫查詢計劃時不小心選擇了錯誤的列,則編譯器不會抱怨,並且僅在運行應用程序時才檢測到錯誤。 對於Spark開發人員來說,這是一個單調乏味的問題,因為它極大地延長了開發過程。 為了解決這個問題,Spark庫引入了數據集。

讓我們總結一下DataFrames的優缺點:

✔️高級抽象

✔️可能運行SQL查詢

✔️用於查詢計劃的Catalyst優化器

✔️鎢優化器用於序列化

❌沒有面向對象的編程

runtime在運行時推斷的類型

數據集 Dataset

數據集可從Spark版本1.6獲得。 像DataFrames一樣,它們是在Spark SQL模塊中引入的。

數據集是分佈式數據集合,結合了RDD的優點和Spark SQL引擎的功能。 實際上,Spark數據集既提供用於轉換的OOP接口,又提供用於運行查詢的SQL。 數據集與RDD非常相似:可以通過從外部源讀取數據或並行化一組Java對象來構造它們。

在處理結構化或半結構化數據時,數據集是一個不錯的選擇:只要在Scala案例類中提供單個數據對象的類型,Spark就會自動推導出表格表示形式。 例如,假設您必須處理賽車期間收集的一些傳感器數據。 理想情況下,假設傳感器在json結構中每秒記錄一次汽車的速度,如下所示:

{"carId": 5, "driver": "S.Vettel", "team": "Ferrari", "lap": 22, "speed": 340, "timestamp": 123456789}

可以從json文件中獲取從參加比賽的所有汽車收集的傳感器數據。 現在,您想從"法拉利"車隊中找到達到最高速度的駕駛員。 您可以使用類似RDD的樣式(請參見代碼段中的版本1)或以SQL方式(請參見代碼段中的版本2)來實現。

在第一種情況下,請按照下列步驟操作:

· 僅過濾(保留)來自"法拉利"傳感器組的線路;

· 按速度降序對數據集進行排序;

· 映射對象以僅接受驅動程序字段;

· 獲取數據集的第一個對象(頭部)。

而SQL查詢是不言自明的。

此外,數據集繼承了DataFrames引入的所有優化。 催化劑和鎢優化器仍在這裡,但有一些改進:編碼器。 編碼器用於將任何Java對象編碼(解碼)為Spark的Row內部格式。 通過生成用於與堆外數據進行交互的字節碼,編碼器可以按需訪問單個數據屬性,而無需反序列化整個Java對象。 結果,Spark數據集增加了內存使用量,並大大加快了程序執行速度。

但是,當需要對象序列化時,數據集會遭受垃圾收集器的開銷。 使用用戶定義的函數(UDF)調用映射和篩選器操作時,嚴格要求對象序列化。 例如,如果您定義一個自定義函數以平方一個數字而不是使用Spark提供的內置函數,則您不會利用所有優化功能。 最後,Spark文檔中許多數據集的功能仍被標記為"實驗性"。

在結束本文之前,我們先總結一下數據集的優缺點:

✔️面向對象的編程風格

✔️編譯時類型安全

✔️可能運行SQL查詢

✔️催化劑和鎢優化器

✔️編碼器可優化序列化

❌對UDF的性能影響

❌垃圾收集器的開銷

❌一些功能仍在試驗中

結論

總結起來,Apache Spark提供了上面概述的三種數據抽象。 當您需要對一組Java對象進行低級控制時,RDD是很好的選擇,而DataFrames和Datasets在結構化數據方面表現出色,並且運行速度極高。

著眼於性能提升與快速開發之間的權衡取捨,最適合您的問題。


(本文翻譯自Federico Sala的文章《Apache Spark: how to choose the correct data abstraction?》,參考:https://medium.com/quantyca/apache-spark-how-to-choose-the-correct-data-abstraction-8df7c6d8ec63)


分享到:


相關文章: