過往記憶大數據 過往記憶大數據
Delta Lake 是今年數磚在 Spark+AI Summit 2019 會議上開源的項目,詳見【重磅 | Apache Spark 社區期待的 Delta Lake 開源了】,當時文章只是簡單介紹了下功能,本文將深刻介紹 Apache Spark Delta Lake 的事務日誌,經過本文咱們能夠了解 Delta Lake 的 ACID 是如何實現的,這些巧妙的設計很是值得咱們學習。
事務日誌是理解 Delta Lake 的關鍵,由於它是貫穿許多最重要功能的通用模塊,包括 ACID 事務、可擴展的元數據處理、時間旅行(time travel)等。本文咱們將探討事務日誌(Transaction Log)是什麼,它在文件級別是如何工做的,以及它如何爲多個併發讀取和寫入問題提供優雅的解決方案。html
Delta Lake 事務日誌(也稱爲 DeltaLog)是 Delta Lake 表上執行每次事務的有序記錄。具體形式以下:數據庫
單一事實來源json
Delta Lake 構建於 Apache Spark™ 之上,容許多個寫和讀操做同時對給定表進行操做。爲了始終向用戶顯示正確的數據視圖,事務日誌可做爲單一事實來源(single source of truth) - 中央存儲庫,用於跟蹤用戶對錶所作的全部更改。緩存
當用戶第一次讀取 Delta Lake 表或在打開的表上運行一個新查詢,該表自上次讀取以來已被修改,Spark 會檢查事務日誌來查看已向表寫入的新事務,而後使用這些新更改更新最終用戶的表。這可確保用戶表的版本始終與最新查詢中的主記錄同步,而且用戶沒法對錶進行不一樣的,衝突的更改。微信
原子性是 ACID 事務的四個屬性之一,它能夠保證在 Delta Lake 上執行的操做(如 INSERT 或 UPDATE )要麼所有成功要麼所有不成功。若是沒有此屬性,硬件故障或軟件錯誤很容易致使數據僅部分寫入表中,從而致使數據混亂或損壞。併發
事務日誌是 Delta Lake 可以提供原子性保證的機制。不管如何,若是它沒有記錄在事務日誌中,它就不會發生。經過只記錄徹底執行的事務,並使用該記錄做爲惟一的真相來源,事務日誌容許用戶對其數據進行推理;而且即便數據在 PB 級別上,咱們也能夠對這些數據的準確性高枕無憂。ide
將事務分解爲原子提交
每當用戶執行修改表的操做(例如插入、更新或刪除)時,Delta Lake 將該操做分解爲一系列由如下一個或多個操做組成的離散步驟:oop
Commit info:包含有關提交的信息以及該操做是在什麼時候何地進行的。
而後這些操做將按照有序的原子單位記錄在事務日誌中,稱爲提交。
例如,假設用戶建立一個事務以向表中添加新列,並向其中添加更多數據。Delta Lake 會將該事務分解爲多個部分,一旦事務完成,就將它們添加到事務日誌中,以下所示:學習
若是想及時瞭解Spark、Hadoop或者HBase相關的文章,歡迎關注微信公衆號:iteblog_hadoop
所以,若是咱們經過從數據文件 1.parquet 和 2.parquet 向表中添加記錄。該事務將自動添加到事務日誌中,並以 000000.json 的形式保存到磁盤。而後,咱們改變主意並決定刪除這些文件並添加一個新文件(3.parquet)。這些操做將記錄爲事務日誌中的下一個提交,也就是 000001.json,以下所示。大數據
若是想及時瞭解Spark、Hadoop或者HBase相關的文章,歡迎關注微信公衆號:iteblog_hadoop
儘管 1.parquet 和 2.parquet 再也不是咱們 Delta Lake 表的一部分,但它們的添加和刪除仍記錄在事務日誌中,由於這些操做是在咱們的表上執行的 - 儘管它們最終相互抵消了。 Delta Lake 仍然保留這樣的原子提交,以確保在須要審計表或使用「時間旅行」來查看錶在給定時間點的樣子時,咱們可以準確地作到這一點。
此外,即便咱們從表中刪除了基礎數據文件,Spark 也不會馬上從磁盤中刪除文件。用戶可使用 VACUUM 命令刪除再也不須要的文件。
一旦咱們提交了10次事務日誌,Delta Lake 就會在相同的 _delta_log 子目錄中以 Parquet 格式保存一個檢查點文件(如上面的 00000000000000000010.checkpoint.parquet 文件)。每 10 次提交 Delta Lake 會自動生成檢查點文件,這個是經過參數 checkpointInterval 參數設置。使用檢查點文件(Checkpoint Files)快速從新計算狀態
若是想及時瞭解Spark、Hadoop或者HBase相關的文章,歡迎關注微信公衆號:iteblog_hadoop
這些檢查點文件在某個時間點保存表的整個狀態 - 以原生的 Parquet 格式保存,Spark 能夠快速輕鬆地讀取。換句話說,它們爲 Spark reader 提供了一種「快捷方式」來徹底複製表的狀態,從而容許 Spark 避免從新處理可能存在的數千個低效的小 JSON 文件。
爲了提升速度,Spark能夠運行一個 listFrom 操做來查看事務日誌中的全部文件,快速跳轉到最新的檢查點文件,而且只處理自保存了最新的檢查點文件以來提交的JSON。
爲了演示這是如何工做的,假設咱們已經建立了提交,而且事務日誌已經記錄到 000007.json。Spark 加快了提交的速度,並在內存中自動緩存了表的最新版本。與此同時,其餘一些寫入者(多是您過於熱心的隊友)已經向表中寫入了新數據,並事務日誌已經記錄到 0000012.json 了。
爲了合併這些新事務並更新表的狀態,Spark 將運行 listFrom 方法來查看版本7以後對錶的新更改。
若是想及時瞭解Spark、Hadoop或者HBase相關的文章,歡迎關注微信公衆號:iteblog_hadoop
Spark能夠直接跳到最近的檢查點文件(上圖中的 0000010.checkpoint.parquet 文件),而不須要處理全部中間 JSON 文件,由於這個檢查點文件包含 commit #10 中表的整個狀態。如今,Spark 只需執行 0000011.json 和 0000012.json 的增量處理便可得到表的當前狀態。而後 Spark 將表的版本12的狀態緩存到內存中。經過遵循此工做流程,Delta Lake 可以使用 Spark 以高效的方式始終更新表的狀態。
如今咱們已經在高層次上了解了事務日誌如何工做的,讓咱們來談談併發性。到目前爲止,咱們的示例主要涵蓋了用戶線性提交事務或至少沒有衝突的狀況。可是當 Delta Lake 處理多個併發讀寫時會發生什麼?
答案很簡單,因爲 Delta Lake 由 Apache Spark 提供支持,所以不只可讓多個用戶同時修改表 - 這是預期的。爲了處理這些狀況,Delta Lake 採用了樂觀的併發控制。
樂觀併發控制是一種處理併發事務的方法,它假定不一樣用戶對錶所作的事務(更改)能夠在不相互衝突的狀況下完成。它的速度快得使人難以置信,由於當處理 PB 級的數據時,用戶極可能同時處理數據的不一樣部分,從而容許他們同時完成不衝突的事務。
例如,假設你和我正在一塊兒玩拼圖遊戲。只要咱們都在作拼圖的不一樣部分——好比你在角落裏,我在邊緣上——咱們沒有理由不能同時作更大拼圖的那一部分,而且以兩倍的速度完成拼圖。只有當咱們同時須要相同的部件時,纔會產生衝突。這就是樂觀併發控制。
相反,一些數據庫系統使用悲觀鎖定的概念,這是假設最壞的狀況——即便咱們有10,000塊拼圖,在某個時候咱們確定須要相同的拼圖——這致使了太多的衝突。爲了解決這個問題,它的理由是,應該只容許一我的同時作拼圖,並把其餘人都鎖在房間外面。這不是一個快速(或友好)解決難題的方法!
固然,即便使用樂觀併發控制,有時用戶也會嘗試同時修改數據的相同部分。幸運的是,Delta Lake 有相應的協議處理它。
爲了提供ACID事務,Delta Lake 有一個協議,用於肯定提交應該如何排序(在數據庫中稱爲 serializability),並肯定在同時執行兩個或多個提交時應該作什麼。Delta Lake經過實現互斥(mutual exclusion)規則來處理這些狀況,而後嘗試樂觀地解決任何衝突。該協議容許Delta Lake遵循ACID隔離原則,該原則確保多個併發寫操做以後的表的結果狀態與那些連續發生的寫操做相同,而且是彼此隔離的。
通常來講,這個過程是這樣進行的
若是想及時瞭解Spark、Hadoop或者HBase相關的文章,歡迎關注微信公衆號:iteblog_hadoop
每一個表都是事務日誌中記錄的全部提交的總和的結果—很少也很多。事務日誌提供了一步一步的指導,詳細描述瞭如何從表的原始狀態轉換到當前狀態。
所以,咱們能夠經過從原始表開始從新建立表在任什麼時候間點的狀態,而且只處理在該點以前提交的數據。這種強大的功能被稱爲「時間旅行」,或數據版本控制,在任何狀況下都是救星。有關更多信息,請參考介紹大型數據湖的 Delta 時間旅行。
做爲對 Delta Lake 表所作的每一個更改的最終記錄,事務日誌爲用戶提供了可驗證的數據血統,這對於治理、審計和合規性目的很是有用。它還能夠用於跟蹤一個意外更改或管道中的一個 bug 的起源,以追溯到致使該更改的確切操做。用戶能夠運行 DESCRIBE HISTORY 來查看所作更改的元數據。
在本博客中,咱們深刻研究 Delta Lake 事務日誌的工做原理。咱們討論了: