隨著大數據處理從本地端轉向雲端原生架構,Amazon S3 等物件儲存服務已成資料湖的標準基石,而 Apache Spark 則為主流的分散式運算引擎。然而,當資料規模擴大時,這種組合暴露出其架構缺陷,包含中繼資料管理瓶頸、缺乏交易保證導致的資料不一致,以及難以執行更新操作等問題。這些挑戰促使業界尋求新的解決方案,最終催生了「湖倉一體」此一新興典範。此架構旨在結合資料湖的彈性與資料倉儲的可靠性,透過如 Delta Lake 的格式引入交易日誌機制,為現代資料工程管線提供更穩固的 ACID 事務與 Schema 管理基礎,有效解決了傳統資料湖的諸多痛點。

第五章:物件儲存與資料湖

理解分散式檔案系統

現在玄貓已經展示了如何使用S3 SDK操作S3中的物件,玄貓將繼續使用Spark來管理大量資料。讓玄貓看看如何在Spark中使用S3

import org.apache.spark.sql.SparkSession
import org.apache.spark.sql.SaveMode

val spark = SparkSession
.builder()
.appName("Spark SQL basic example")
.config("fs.s3a.aws.credentials.provider","com.amazonaws.auth.profile.ProfileCredentialsProvider")
.master("local[*]")
.getOrCreate()

這段程式碼初始化了一個SparkSession,它是與Spark互動的入口點。關鍵配置是fs.s3a.aws.credentials.provider,它指定了Spark如何獲取訪問S3的憑證。這裡使用的是ProfileCredentialsProvider,表示Spark將從本地AWS設定檔中讀取憑證。master("local[*]")表示Spark將在本地模式下運行,使用所有可用的CPU核心。

然後,Spark Session可以用來從物件儲存中讀取資料並執行操作,例如顯示DataFrame的內容,如下所示:

spark.read.option("header","true").csv("s3a://scala-data-engineering/my/first/").show()

這段程式碼展示了如何使用Spark從S3讀取CSV格式的資料。s3a://scala-data-engineering/my/first/是一個S3路徑,Spark會讀取該路徑下所有物件。option("header","true")表示CSV檔案包含標頭行。show()方法用於顯示DataFrame的內容。

Spark可以接受前綴位置並處理該位置內的所有物件,而不是具體指定物件鍵。上述簡單範例使用了CSV讀取器,但Spark為不同的物件類型提供了許多抽象,包括二進位、CSV、文本、Avro、Parquet和ORC。

現在玄貓已經展示了如何從S3讀取資料,玄貓還需要在資料工程管線中寫入資料。為了將資料寫回物件儲存,Spark使用write方法,並可以將檔案以其中一種支援的格式放回儲存中。讓玄貓使用以下程式碼來完成此操作:

val DF = spark.read.option("header","true").csv("s3a://scala-data-engineering/my/first/")

DF.write.csv("s3a://scala-data-engineering/my/first_write")

這段程式碼首先從S3讀取一個CSV檔案到一個DataFrame中,然後將這個DataFrame寫回S3的一個新路徑s3a://scala-data-engineering/my/first_write,同樣以CSV格式儲存。

透過Spark,這段程式碼將在必要時創建前綴,並將資料作為一個或多個物件附加到前綴。創建的物件數量將取決於Spark處理在寫入之前產生的分割區數量。在這個簡單的範例中,它將產生一個單一的輸出物件,但如果來源物件很大,那麼Spark會將讀取分割成多個分割區以實現平行化。為了截斷前綴並覆寫所有資料,可以使用mode方法:

DF.write.mode(SaveMode.Overwrite).csv("s3a://scala-data-engineering/my/first_write")

這段程式碼與前一個寫入操作的區別在於使用了mode(SaveMode.Overwrite)SaveMode.Overwrite指示Spark在寫入資料之前,如果目標路徑存在,則刪除該路徑下的所有內容。這對於需要定期更新或替換整個資料集的場景非常有用。

現在玄貓已經展示了如何讀寫資料到S3,讓玄貓來談談物件儲存的彈性

物件儲存的彈性

物件儲存是分散式大資料儲存的下一個演進。它們提供豐富的API用於操作,並具有極高的儲存持久性保證。這些持久性保證是透過多重備份和分散式儲存機制實現的,因此即使單個儲存陣列發生故障,其他陣列也能完全透明地響應請求,而使用者無從感知。

可以配置額外的全域複製,以在災難恢復場景中將資料冗餘地複製到不同區域。這些是透過在儲存桶或容器級別進行的簡單配置,由雲端供應商負責資料的實體複製。除了以持久方式提供幾乎無限的儲存空間外,物件儲存還利用規模經濟以極低的成本提供這些服務。對於AWS S3,每TB儲存的成本約為23美元。

物件儲存解決了早期Hadoop時代的許多問題,但隨著公司大規模採用它們,它們的能力很快就出現了裂痕。這些裂痕導致公司尋找利用物件儲存的新方法,即湖倉一體

深入探討湖倉一體

資料湖的使用之所以能夠真正擴展,是因為物件儲存的引入。如果沒有雲端供應商的物件儲存資料湖將面臨本地(on-prem)儲存的可擴展性問題。Apache Spark的引入透過易於使用的API和DataFrame抽象,使得大資料處理變得可行。然而,這些技術並非沒有其自身的缺點。

資料湖的局限性

為了讀取一個前綴,Spark必須首先列出該位置中的所有物件。這是一個單節點操作,在需要從包含數百萬個檔案的前綴讀取的作業中,這將成為一個巨大的瓶頸。在某些情況下,這甚至會導致驅動節點崩潰。

此圖示:Spark與S3互動流程

@startuml
!define DISABLE_LINK
!define PLANTUML_FORMAT svg
!theme _none_

skinparam dpi auto
skinparam shadowing false
skinparam linetype ortho
skinparam roundcorner 5
skinparam defaultFontName "Microsoft JhengHei UI"
skinparam defaultFontSize 16
skinparam minClassWidth 100

actor "資料工程師" as DE

box "Spark 應用程式" #LightBlue
participant "SparkSession" as SS
participant "DataFrameReader" as DFR
participant "DataFrameWriter" as DFW
participant "DataFrame" as DF
end box

cloud "AWS S3" as S3Cloud {
folder "s3a://bucket/path/" as S3Path
file "object1.csv" as Obj1
file "object2.csv" as Obj2
file "object_output/" as S3Output
}

DE -> SS : 建立 SparkSession
activate SS
SS -> SS : 配置 S3 憑證 (fs.s3a.aws.credentials.provider)
SS -> DFR : 呼叫 spark.read
activate DFR
DFR -> S3Cloud : 讀取 "s3a://bucket/path/" (例如 CSV)
activate S3Cloud
S3Cloud --> DFR : 返回物件內容 (Obj1, Obj2)
deactivate S3Cloud
DFR --> DF : 建立 DataFrame
deactivate DFR
DF --> DE : 顯示或進一步處理 (.show(), .filter(), .write())

DE -> DF : 呼叫 .write
activate DFW
DF -> DFW : 準備寫入 (例如 .csv("s3a://output/"))
DFW -> S3Cloud : 寫入資料到 "s3a://output/"
activate S3Cloud
S3Cloud --> DFW : 儲存為物件 (例如 part-00000.csv)
deactivate S3Cloud
deactivate DFW
deactivate SS

note right of DFR
支援多種格式: CSV, Parquet, ORC, JSON, etc.
可設定選項: header, inferSchema
end note

note right of DFW
可設定寫入模式: Overwrite, Append, Ignore, ErrorIfExists
可設定分割區數量: .repartition()
end note

@enduml

看圖說話:

此圖示展示了資料工程師如何透過Spark應用程式與AWS S3進行互動的流程。首先,資料工程師啟動一個SparkSession並配置S3的存取憑證。接著,透過DataFrameReader,Spark能夠從S3指定路徑讀取資料,並將其轉換為DataFrame進行處理。這個讀取過程可以處理多個物件,並支援多種檔案格式。完成資料處理後,資料工程師可以透過DataFrameWriter將處理後的DataFrame寫回S3的指定輸出路徑。在寫入過程中,可以選擇不同的寫入模式(如覆寫、追加)以及控制輸出檔案的分割區數量。整個流程清晰地描繪了Spark如何作為一個強大的工具,無縫地整合物件儲存,實現大規模資料的讀取、處理和寫入,極大地簡化了雲端資料工程管線的建構。

第五章:物件儲存與資料湖

資料湖的局限性

它無法在記憶體中容納所有中繼資料。這被稱為中繼資料擴展問題。簡單的列表操作可能會產生幾乎與實際資料本身一樣大的記憶體佔用。

資料物件沒有Schema強制執行。寫入者可以將任何資料放入前綴中,而無需檢查,如果引入了破壞性的Schema變更,則會導致該資料的消費者失敗。

一個寫入者可能將一個欄位寫為整數,而另一個寫入者則將資料添加為小數。這將導致生產管線失敗,並且對於值班工程師來說,要找出到底是哪個或哪些物件導致了問題,清理它們,然後重新啟動管線,將會非常繁瑣。

標準資料操作是一個難題。由於物件只能被創建或覆寫,因此沒有簡單的方法來執行刪除、更新或合併等操作。為了更改記錄,需要讀取整個記錄集,在記憶體中更新,然後再次完全替換。這導致了當寫入者在追加、覆寫或嘗試使用覆寫操作資料時,會出現一致性問題。讀取者將無法與這些操作隔離,並且會獲得部分或髒讀。這也意味著小型物件無法安全地組合以產生讀取效率所需的大型物件,並導致了中繼資料擴展問題

物件儲存也無法提供傳統的增強功能,例如索引、時間旅行、回溯和變更饋送。所有上述問題,加上缺乏增強功能,使得工程師需要創建穩健的處理管線,這些管線可以廉價地擴展,但最終結果卻需要複製到輔助基礎設施中,才能將資料提供給業務使用者消費。報告需要不能意外更改、一致且高效能的資料。這在傳統資料湖格式中是不可能實現的。

為了克服這些挑戰,需要超越Parquet、ORC和Avro等新格式。組織開始開發後來被稱為湖倉一體格式的解決方案來解決這些問題。

湖倉一體能帶來什麼?

湖倉一體是一種新興的技術範式,旨在統一資料工程、資料科學和機器學習以及商業智慧的資料格局。

為了解決物件儲存資料湖之間的差距,開發了三個主要的抽象。2016年,Uber開發了自己的內部儲存格式,稱為Hudi,以解決其內部管理資料湖的困境。2020年,它成為一個頂級的Apache專案。另一個湖倉一體格式Apache IcebergIceberg最初由Netflix於2017年在內部開發,旨在解決前幾段中強調的內部困境。它被捐贈給Apache軟體基金會,並於2020年成為一個頂級專案。然而,第一個商業開發的湖倉一體格式,稱為Delta Lake (Delta),由Databricks於2019年為其客戶開發。

這種格式在Databricks的廣大商業客戶中經過了實戰考驗,然後於2019年晚些時候透過Linux基金會開源。Delta Lake在許多組織中得到了最廣泛的使用,因為它最初並不是作為一個內部公司專案開發的。這使得Delta有機會開發出廣泛的功能,這些功能針對大量問題進行了通用化解決。

Delta改變了大資料處理在廉價、持久的物件儲存上發生的方式。Delta使用Parquet檔案作為其在物件儲存上的底層儲存機制,但在其之上添加了一個中繼資料交易日誌,該日誌與Parquet檔案一起儲存。交易日誌充當讀寫器互動的可擴展介面,而無需打開Parquet檔案。這個中繼資料層將告訴應用程式有關底層Parquet檔案的資訊,例如資料表當前版本中有哪些檔案、資料中欄位的統計資訊、資料表的Schema以及資料表中不同版本的資訊。這個交易日誌是描述所有這些資訊的JSON檔案。它會定期檢查點並儲存為Parquet以加快中繼資料檢索。對Delta資料表的寫入要麼完全提交,要麼失敗,而不會提交新的交易條目。請參閱圖5.1以了解這在物件儲存上的外觀。

此圖示:資料湖的局限性與湖倉一體解決方案

@startuml
!define DISABLE_LINK
!define PLANTUML_FORMAT svg
!theme _none_

skinparam dpi auto
skinparam shadowing false
skinparam linetype ortho
skinparam roundcorner 5
skinparam defaultFontName "Microsoft JhengHei UI"
skinparam defaultFontSize 16
skinparam minClassWidth 100

package "傳統資料湖 (基於物件儲存)" as OldDataLake {
component "中繼資料擴展問題" as MetadataScaling
component "無 Schema 強制" as NoSchemaEnforcement
component "資料操作困難 (CRUD)" as DifficultCRUD
component "讀取隔離性差 (髒讀)" as DirtyReads
component "缺乏索引/時間旅行" as NoAdvancedFeatures
MetadataScaling --> NoSchemaEnforcement
NoSchemaEnforcement --> DifficultCRUD
DifficultCRUD --> DirtyReads
DirtyReads --> NoAdvancedFeatures
}

package "湖倉一體 (Lakehouse)" as LakehouseSolution {
component "統一資料格局" as UnifiedDataLandscape
component "交易日誌 (Transaction Log)" as TransactionLog
component "Parquet 檔案 (底層儲存)" as ParquetStorage
component "Schema 強制與演進" as SchemaEnforcement
component "ACID 事務保證" as ACID
component "時間旅行與版本控制" as TimeTravel
component "優化資料操作 (Upsert/Delete)" as OptimizedCRUD
component "索引與查詢優化" as Indexing

TransactionLog --> ParquetStorage : 描述底層檔案
TransactionLog --> SchemaEnforcement : 儲存 Schema
TransactionLog --> ACID : 提供事務保證
TransactionLog --> TimeTravel : 記錄版本歷史
UnifiedDataLandscape --> TransactionLog
UnifiedDataLandscape --> SchemaEnforcement
UnifiedDataLandscape --> ACID
UnifiedDataLandscape --> TimeTravel
UnifiedDataLandscape --> OptimizedCRUD
UnifiedDataLandscape --> Indexing
}

OldDataLake --> LakehouseSolution : 解決痛點

note right of TransactionLog
JSON 檔案,定期檢查點為 Parquet
包含檔案列表、統計、Schema、版本資訊
end note

note right of ParquetStorage
優化列式儲存格式
end note

@enduml

看圖說話:

此圖示清晰地呈現了傳統資料湖所面臨的挑戰,以及湖倉一體如何透過其創新架構來克服這些局限性。傳統資料湖因中繼資料擴展問題缺乏Schema強制資料操作困難讀取隔離性差以及缺乏進階功能而飽受詬病。這些問題導致資料品質不佳、管線不穩定且難以維護。為了解決這些痛點,湖倉一體應運而生。它透過引入交易日誌作為核心組件,與底層的Parquet檔案協同工作。交易日誌不僅記錄了資料表的Schema、統計資訊和版本歷史,還提供了ACID事務保證,實現了Schema強制與演進時間旅行優化的資料操作以及索引與查詢優化等功能。這種架構有效地統一了資料工程、資料科學和商業智慧的資料格局,為大數據處理提供了更可靠、高效且易於管理的解決方案。

第五章結論

縱觀現代資料架構的演進路徑,物件儲存雖以其彈性與成本效益奠定了資料湖的基礎,卻也同時揭示了其內在的結構性限制。傳統資料湖在面對中繼資料擴展、缺乏Schema強制、以及事務一致性的挑戰時,其效能與可靠性瓶頸日益凸顯,導致資料管線脆弱且維護成本高昂。

湖倉一體的出現,特別是以Delta Lake為代表的交易日誌機制,並非單純的技術疊加,而是一次深刻的架構再造。它巧妙地將物件儲存的規模優勢與資料倉儲的ACID事務、Schema管理及時間旅行等高階功能予以整合,從根本上解決了資料的可靠性與可管理性難題。此一突破預示著資料處理的典範轉移:未來的資料平台將不再是資料湖與資料倉儲的拼湊,而是一個原生統一的環境,大幅降低資料工程、商業智慧與機器學習之間的工作流摩擦。

玄貓認為,對於追求資料資產長期價值的高階管理者而言,導入湖倉一體不僅是技術升級,更是建立一個兼具韌性、敏捷性與治理能力的資料核心資產的策略性投資。