Delta 初探

Delta Lake

在說 Delta Lake 之前,要先提一下 Data Lake ,Data Lake 的主要思想是將企業中的所有數據進行統一管理。例如基於 Hadoop 的 Data Lake 方案可以非常低成本的存儲所有類型的數據。


基於 hadoop 的方案只支持批量插入,且用戶讀取時可能無法獲取最新數據,多用戶同時進行寫操作還會發生異常,帶來髒數據的問題,並不可靠,並且更新及刪除操作非常困難 (需要整塊重寫),無法保證數據一致性 (例如 spark 讀取時緩存了 parquet 元數據,若元數據變化需要進行 refresh)。​


Delta Lake 不僅能解決上述問題,還能對數據進行各種增強,例如 time travel 等。

Delta Lake 在近期由 Databricks 開源併發布了其第一個 release 版本,藉助了開源社區的力量,底層使用 parquet 文件格式,在其上通過 transaction log (json 格式) 記錄更新操作從而提供了 ACID 事務等能力,通過與 spark 集成,得以處理大量的元數據。

Delta Lake 可以更簡單的理解爲構建在 Data Lake 上的一層數據庫。巧妙的地方在於它不是一種 file format 而是 table format ,這意味着你不需要修改底層的存儲文件,它解放了用戶花費在數據一致性上的大量時間,進行數據分析時也無需關心表中的數據是來自流數據還是歷史數據。此外,通過時間旅行的功能,還可以實現事件回放等​功能。​
Delta Lake
Delta lake 相關特性在官網上已經說的非常詳細,不再贅述,根據筆者自己的理解簡單描述一下五個比較重要的特性:

  1. ACID transactions:在數據上的操作變得可靠,因此能提供 Update/Delete/Merge into 的能力。​
  2. Schema enforcement: 提供了指定模式並強制執行它的能力。這有助於確保數據類型正確並且存在必需的列,防止錯誤數據導致數據損壞。​
  3. Streaming and batch unification:統一了流和批處理,在以前,通過 spark streaming 可以處理當前事件,但是無法處理歷史數據或是進行一些複雜的機器學習分析。這個時候大家可能會想到 lambda 架構,但是其實現及運維卻異常繁瑣。​在 Delta Lake 中,不論表是流式追加還是靜態,不同的用戶同時對它進行讀寫操作,都是透明的,不會發生異常和併發問題。這意味着你可以直接查詢當前最新的數據或是歷史的數據。
  4. Scalable metadata handling:在大數據場景,元數據本身也可能成爲大數據(例如一些大分區表的元數據)。如果將大量的元數據操作放在傳統的 hive metastore 執行,可能會非常緩慢,因此可以通過 Spark 強大的分佈式處理能力來解決這個問題(可以像處理數據一樣處理元數據)。​
  5. Time travel:時間旅行,它意味着用戶可以提供時間戳對早期版本的數據進行審覈、回滾或重現。比如可以應用在機器學習的事件回放場景(不用擔心由於數據更新導致多次運行時 input 數據不一致)。

Transactional metadata 實現

在文件上增加一個日誌結構化存儲(transaction log),該日誌有(ordered) 且保持原子性(atomic)。​

增加或者刪除數據時,都會產生一條描述文件(commit),通過採用樂觀併發控制 (optimistic concurrency control) 保證用戶併發操作時數據的一致性。

併發控制

Delta Lake 在讀寫中提供了 ACID 事務保證:

  • 即使多個任務同時對一張表進行寫入操作,此時也能看到表的一致性快照,同時這些寫入操作將會根據時序串行執行。
  • 即使在 spark 作業執行期間修改了數據,任務也能讀取一致性快照。

樂觀併發控制(optimistic concurrency control)

Delta Lake 使用 optimistic concurrency control 機制提供寫數據時的事務保證,寫過程包含三個步驟:

  1. Read: 讀取表最新可用版本,以確定哪些文件需要被重新定義。
  2. Write: 將所有的修改寫成新的數據文件。
  3. Validate and commit: 在提交最終變更之前,檢查自讀取該快照以來,是否和其他併發提交的變更發生衝突。如果沒有衝突,所有的變更會被提交併生成一個新版本快照,寫操作成功。如果發現了衝突,該寫入操作會拋出異常後失敗,而不會像開源版的 spark 一樣破壞數據。

使用

在 spark 中,爲了提高查詢性能,在讀取表時會緩存其文件的元數據(包含一些路徑信息),一旦表進行了更新操作,必須要手動刷新元數據(例如 spark.sql(refresh table xxx) )才能保證數據讀取到最新數據,如果是刪除操作,還會觸發文件找不到錯誤​。

因爲 delta Lake 的特性,所以無需在數據更新後調用 refresh,目前僅支持 HDFS 作爲底層存儲

// 將已有 parquet 數據轉化爲 delta table 
val df = spark.read.parquet("/path/to/your/data")
df.write.format("delta").save("/delta/delta_table")
spark.sql("CREATE TABLE delta_table USING DELTA LOCATION '/delta/delta_table'")

delta 表根目錄 _delta_log 下會生成 json 格式的事務文件

hdfs dfs -cat /delta/delta_table/_delta_log/00000000000000000000.json

{"commitInfo":{"timestamp":1555989622032,"operation":"WRITE","operationParameters":{"mode":"ErrorIfExists","partitionBy":"[]"}}}
{"protocol":{"minReaderVersion":1,"minWriterVersion":2}}
{"metaData":{"id":"f33f4754-7c0c-43b8-87a2-ddc21e1311ea","format":{"provider":"parquet","options":{}},"schemaString":"{\"type\":\"struct\",\"fields\":[{\"name\":\"id\",\"type\":\"integer\",\"nullable\":true,\"metadata\":{\"comment\":\"\"}}]}","partitionColumns":[],"configuration":{},"createdTime":1555989621467}}

結語

Delta Lake / Iceberg 使用 table format 的方式能夠解決大數據處理的很多痛點。受限於 spark2.4 的 sql 的限制,目前 DML 只支持 api 的方式調用,後續的 sql 支持需要等到 spark3 release 後社區進行支持。

項目地址

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章