Delta Lake 初探

Delta Lake

在說 Delta Lake 之前,不得不說下 Data Lake ,Data Lake 的主要思想是將企業中的所有數據進行統一管理。例如基於 Hadoop 的 Data Lake 方案可以非常低成本的存儲所有類型的數據,但是顯而易見的,它只支持批量插入,用戶讀取時無法獲取當前更新的數據,多用戶寫還可能會發生異常,數據並不是非常可靠。

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

在閱讀文章之前,你是否在hadoop生態的工作中遇到過這些問題?

  1. 困難的更新/刪除操作 (需要整塊重寫)
  2. 無法保證數據一致性 (例如 spark 緩存了 parquet 元數據需要 refresh)
  3. etc.

Delta Lake 在近期開源 項目地址,發佈了其第一個 release 版本,使用 parquet 文件格式,在其上通過 transaction log 記錄更新操作從而提供了 ACID 事務等能力,通過與 spark 集成,得以處理大量的元數據。

Delta Lake 可以更簡單的理解爲構建在 Data Lake 上的一層數據庫。巧妙的地方在於,它不是一種 file format 而是 table format ,這意味着你不需要修改底層的存儲文件,他解放了用戶花費在數據一致性上的大量時間,分析時也無需關心表中的數據是來自流數據還是歷史數據。此外,通過時間旅行的功能,可能解決很多非常麻煩的事情,例如事件回放。

Delta Lake

Delta lake 相關特性在官網上已經說的非常詳細,不再贅述,根據筆者自己的理解簡單描述一下五個比較重要的特性:

  1. ACID transactions,在數據上的操作變的可靠。
  2. Schema enforcement, 自動處理表變化,例如 shema 發生變化的數據插入會被拒絕。
  3. Streaming and batch unification,統一了流和批處理,在以前,通過 spark streaming 可以非常容易的處理當前事件,但是無法處理歷史數據或是進行一些複雜的機器學習分析。這個時候大家很容易就能想到 lambda 架構,在 Delta Lake 中,不論表是流式追加還是靜態,不同的用戶同時對它進行讀寫操作,都是透明的,不會發生異常和併發問題。這意味着你可以直接查詢當前最新的數據或是歷史的數據。
  4. Scalable metadata handling,隨着數據的增加,元數據也會變得非常大,甚至可能會超過數據本身。顯而易見會帶來元數據恢復的問題(可以想象一下namenode啓動時緩慢的元數據讀取過程),因此把超大的元數據存放在 hive metastore 或是其他的系統中並不現實,同時非常難以管理。因此可以通過 Spark 強大的分佈式處理能力來解決這個問題,例如輕鬆處理pb級表的所有元數據和數十億個文件(類似 parquet 的分區發現)。
  5. Time travel,時間旅行?這樣說可能有點抽象,它意味着你可以提供時間戳回朔到當時的數據狀態。這在機器學習之類的事件回放場景太有用了(不用擔心由於數據更新導致多次運行時 input 數據不一致)。其他的一些場景例如數據版本回滾(類似 git reset),完整的歷史審計日誌跟蹤等。

Transactional metada 實現

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

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

併發控制

Delta Lake 在讀寫中提供了 ACID 事務保證。這意味着:

  • 即使是跨多個集羣的併發寫入,也可以同時修改數據集並查看錶的一致性快照,這些寫入操作將按照串行執行
  • 即使在作業執行期間修改了數據,讀取時也能看到一致性快照。

樂觀併發控制(optimistic concurrency control)

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

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

使用

在 spark 中,爲了提高查詢性能會緩存 parquet 的元數據,如果此時表的 schema 被更新必須要手動刷新元數據才能保證數據的一致性,例如 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}}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章