JuiceFS 在資料湖儲存架構上的探索

語言: CN / TW / HK

文章根據 Juicedata 技術專家高昌健,在  DataFunSummit  大資料儲存架構峰會 所作主題演講《 JuiceFS 在資料湖儲存架構上的探索 》整理而成,現場影片可點選 閱讀原文 檢視。

大家好,我是來自 Juicedata 的高昌健,今天想跟大家分享的主題是《JuiceFS 在資料湖儲存架構上的探索》,以下是今天分享的提綱:

首先我會簡單的介紹一下大資料儲存架構變遷以及它們的優缺點,然後介紹什麼是 JuiceFS,其次的話會再重點介紹一下關於 JuiceFS 和資料湖的一些結合和關聯,最後會介紹一下 JuiceFS 和資料湖生態的整合。

大資料儲存架構變遷

縱觀整個大資料儲存架構的變遷,可以看到有非常明顯的三個階段:第一個階段就是從最早的 Hadoop、Hive 等專案誕生之後,有了 資料倉庫 (Data Warehouse)的概念。隨著數倉的逐步發展,同時有了雲的誕生,物件儲存的誕生,以及大資料與 AI 的時代到來之後, 資料湖 (Data Lake)這個概念就被凸顯了出來。最近兩三年有一個新的概念,或者是說到了一個新的階段叫做 湖倉一體 (Lakehouse)。傳統數倉大家都比較瞭解,今天會著重看一下後面這兩個階段,也就是資料湖和湖倉一體。

為什麼要有「資料湖」?

資料湖很重要的一個誕生契機,其實是解決資料孤島(Data Silos)問題。產生資料孤島的根本原因,來自於不同的業務或者不同團隊,因為一些歷史原因造成了資料之間其實是一個孤島或者互相之間沒有辦法去做連線。

隨著不同業務的引入,在企業內部資料的格式會變得越來越多樣,除了最早的傳統的結構化資料以外,會發現還有很多半結構化的甚至是非結構化的資料。這些半結構化和非結構化資料也希望能逐步引入到整個公司的資料管理或者運維裡面來,傳統數倉的架構或者說儲存的模型此時就沒有辦法去滿足這種多樣性的資料格式的儲存需求。

然後第三點是分散的資料管理,這點其實是跟第一點資料孤島也是有關聯的。因為你的資料是分佈或者分散在很多不同的地方的,資料的管理或者一些許可權的控制上,也會相對的分散。這個時候你如果要去針對不同的業務與不同的團隊去做管理,也會是一個比較大的工作量。

第四點是儲存與計算的耦合(簡稱「存算耦合」),也是跟傳統 Hadoop 的架構有關,傳統的像 HDFS、YARN 的架構,是針對存算耦合架構來設計的,但在對於現在基於公有云的大資料架構來說,這種存算耦合的架構就比較缺乏彈性了,不管是在運維的彈性上,還是對成本的控制上。

最後一點隨著 AI 行業的發展,在機器學習或深度學習這塊的資料加入進來之後,也是希望能夠在數倉或者說整個大資料架構裡面為基於機器學習或深度學習的業務提供更好的支援。不僅是儲存資料,例如還需要對接深度學習的框架,所以就要提供一些介面的支援,比如 POSIX 等對演算法工程師更友好的方式,而不是傳統的通過 SQL 或一些其它的方式來提供給業務團隊。

什麼是「資料湖」?

這裡引用維基百科上的一句簡介:

A data lake is a system or repository of data stored in its natural/raw format, usually object blobs or files.

其中一個比較重要的定義是 natural/raw format(原始格式) ,跟傳統數倉比較大的區別是我們會傾向於把資料以原始的格式先存到資料湖裡面來。數倉其實也還是存在的但它是一個後置的過程,為了實現這樣一個數據湖,最根本的是需要一個足夠便宜且能夠支援海量資料規模的底層儲存。目前看下來在雲上的話,物件儲存是一個非常好的選擇,它既做到了便宜可靠,同時也能夠支援海量的資料。但物件儲存也不是一個絕對的方案,後面會詳細地去做一些比較。

簡單來說就是「 Everything in one place 」,意思是所有資料都先放到資料湖裡面來,你要做數倉也好,做一些其他的後置 ETL 也好,那是下一個階段的事情,但前提是要把所有的資料都放在一起。「後置 ETL」的意思是說 ETL 依然存在也依然需要,只是它變成了一個後置的流程。因為用到了物件儲存,以及存算分離的架構,所以在整個的架構設計上也會更加的雲原生 。

為什麼要有「湖倉一體」?

在整個資料湖的架構裡面數倉依然是存在的 ,但是它在整個 pipeline 的階段被後置了,必然就會帶來一些資料的滯後。同時傳統的像 Hive 這些元件,其實你要做到近實時或者基於 Hive 來做增量的資料更新是比較麻煩的,特別是如果你要把分割槽(partition)的時間視窗縮得很短的話。

之前提到的機器學習和深度學習的結合問題,在資料湖階段也還是存在。雖然有了資料湖,但對於整個深度學習這塊的支援也還是不太夠,所以在湖倉一體這個階段依然是需要解決的一個問題。

然後就是資料重複拷貝和重複 ETL,因為 ETL 是後置的,數倉也是後置的,所以有很多資料有可能是會從湖裡面再同步或複製到數倉裡面,就會帶來一些資料的重複拷貝或者重複 ETL,重複兩次甚至三次都有可能。

最後就是基於物件儲存這樣的儲存型別,希望能夠提供更多高階特性的支援,比如 ACID 事務、多版本資料、索引、零拷貝克隆等。

什麼是「湖倉一體」?

湖倉一體有一些關鍵的因素,其中第一個是需要一個統一開放的底層檔案格式,這個格式比如說可以是 Parquet、ORC 等業界公認的格式。第二點我們需要一個開放的儲存層,具體來講是類似 Delta Lake、Iceberg、Hudi 的一些開源元件。第三點是要有開放的計算引擎整合,不管你使用哪一種儲存,都需要能夠支援上面多種多樣的計算引擎,而不是把使用者或者業務團隊限定在某一個引擎裡面,不管用 Spark 也好,Presto 也好,用其它的商業引擎也好,可以做到多樣化的支援。最後一點就是和深度學習框架的結合,這裡拿 Uber 開源的 Petastorm 專案舉例,Petastorm 是為 TensorFlow、PyTorch 等框架提供 Parquet 格式讀寫支援的元件,目前初步做到了一些對深度學習框架的支援。

JuiceFS 簡介

JuiceFS 一個開源的雲原生分散式檔案系統,為雲環境設計,提供完備的 POSIX、HDFS 和 S3 API 相容性。使用 JuiceFS 儲存資料,資料本身會被持久化在物件儲存(例如 Amazon S3),相對應的元資料可以按需持久化在 Redis、MySQL、TiKV 等多種資料庫中。目前在 GitHub 上已經有超過 5000 個 star,也有超過 50 個外部貢獻者來一起參與這個專案的維護。

JuiceFS 從架構設計上來說,更傾向於開放結合的態度。眾所周知檔案系統裡面最重要的一個元件就是元資料引擎,JuiceFS 希望能夠結合已有的開源專案,比如說 Redis、SQL 資料庫、分散式 KV 等,把它們納入進來作為整個 JuiceFS 架構裡面的一個元件。在資料儲存方面,目前 JuiceFS 也已經支援超過 30 種底層的儲存系統,除了最主要的物件儲存,還支援像 Ceph、MinIO、Ozone 這樣開源的元件。同時 JuiceFS 也是一個跨平臺的元件,在 Linux、macOS、Windows 上也都可以直接原生的執行。

在 Kubernetes 的環境裡,JuiceFS 提供了原生的 CSI Driver,可以直接通過 Kubernetes 的 PV 或 PVC 的方式直接 mount 到 pod 裡。最後就是一些更高階的特性,比如說資料快取、加密、壓縮、回收站、配額等,目前 JuiceFS 的開源社群裡也有很多的團隊和公司已經在生產環境中使用,例如小米、理想汽車、Shopee、知乎、火山引擎、網易遊戲、攜程等。

上圖主要分了三塊,一個是 Metadata Engine 也就是檔案系統的元資料引擎,所謂元資料引擎就是要儲存整個檔案系統的元資訊,比如檔案的名字、大小以及許可權資訊和目錄結構等。這裡 JuiceFS 更希望和一些成熟的開源的並且大家日常會使用到的資料庫做結合,所以上圖列舉了一些常用的資料庫,都可以作為 JuiceFS 的元資料引擎。

Data Storage 部分是 JuiceFS 底層需要依賴的一個數據儲存,我們沒有重複造輪子,而是選擇了站在已有儲存的肩膀上。雲上的話物件儲存是一個非常好的基礎設施,大家都知道它有很多好處,例如低成本、高吞吐、高可用性。如果你是在 IDC 或者機房裡面也可以有類似的基礎設施提供,JuiceFS 作為使用方,可以直接把這些 Data Storage 對接上,並原生地把它作為整個檔案系統的底層資料儲存。

最上面的話就是客戶端(Client),也就是 JuiceFS 的使用者會直接接觸到的這部分。通過不同的介面,讓使用者在不同的環境與不同的業務裡面都可以訪問到 JuiceFS,使用者不用擔心在不同的使用環境下會出現一些不一致的情況,只需要關心用哪個最熟悉的介面去訪問就好了。

上圖展示的是一個檔案通過 JuiceFS 最終儲存到物件儲存上的一個流程,JuiceFS 會對一個檔案做三個級別的拆分,就是最右邊這一列的 Chunk、Slice、Block 三個級別。

首先 JuiceFS 預設會按照固定的 64MB 大小,把一個檔案按照這個粒度來拆分成很多的 Chunk,然後每個 Chunk 內部的話又可能會有很多這種不同個數、不同長度的 Slice 來構成,每個 Slice 最終又會由很多定長的 Block 來構成。Block 的大小使用者是可以配置的,預設情況下推薦使用 4MB 作為 Block 的大小。最終 Block 經過可選的比如壓縮或者加密之後,再上傳到物件儲存裡面,所以如果你直接去看物件儲存裡儲存的資料的話,是不會看到原始檔案的。比如說你的原始檔案可能是 1G 大小的檔案,但其實在物件儲存上去看的話,會看到很多小的 4MB 的 Block。

需要特別指出的一點是如果檔案本身就是小於 4MB 的,比如一張圖片只有 100KB,這時 JuiceFS 是不會補齊到 4MB 的,還是會按照它原始的大小,檔案是 100KB 最終儲存到物件儲存上也還是 100KB,不會補齊,不會佔用額外的空間。

最後講一下 JuiceFS 為什麼要對檔案儲存格式去做分級。首先是需要基於物件儲存來支援一些高階的特性,比如說隨機寫入;其次對於不同的讀寫訪問模式,通過分塊之後也可以提升效能,比如說在併發寫入或併發讀取上能夠做到更好的效能優化。

JuiceFS 與 HDFS、物件儲存的比較

從儲存規模上來說,其實大家都知道 HDFS 的 NameNode 在單 namespace 上是有儲存上限的,一般來說到億級別這個量級就差不多了,但如果你要儲存更多的資料,你可能要做 federation 或者說一些其它的方式去擴充套件。對於物件儲存和 JuiceFS 來說,是可以非常輕鬆的支撐到百億級甚至更大的儲存規模。

然後在一致性上對於檔案系統來說,之前不論使用 HDFS 或者說其它的檔案系統,預設情況下,都是希望檔案系統提供的是強一致性的保證。但是因為物件儲存的興起之後,會發現最終一致性反而會是一個更常見的情況。不過目前也有一些物件儲存,比如 S3 已經支援了強一致性。

在容量管理上 HDFS 是需要手動擴縮容的方式,所以你沒有辦法在雲上做一個非常彈性的容量管理,但是反觀物件儲存和 JuiceFS 的話,在容量管理上是可以做到非常彈性的,按量付費,大幅節約了儲存成本。

其它幾個特性對於大資料場景也是比較關鍵的,比如說原子重新命名、List 效能、隨機寫、併發寫等,這些特性對於傳統的 HDFS 都是預設支援的,但對於物件儲存來說,有些特性它是部分支援的,有些特性完全無法支援。因為 JuiceFS 本身是一個完備的檔案系統,所以這些特性都是具備的。

快取加速這塊,其實在 HDFS 或物件儲存上目前都還是不具備的一個功能,需要結合一些外部元件來實現,但 JuiceFS 本身已經內建了這個特性。

最後就相容性來說,物件儲存可以用一些社群的元件去通過 HDFS 的 API 訪問,但目前暫時無法做到完全的相容。包括 POSIX 這塊,雖然你可以用到如 S3FS 或者一些其它元件以 POSIX 的介面來訪問物件儲存,但它也只能達到一個部分相容的狀態。對於 JuiceFS 來說是完全相容 HDFS 和 POSIX 介面的。

這裡我們拿 HDFS 裡面的一個元件 NNBench 做了元資料的效能比較,上圖對比的是元資料請求延遲,越低越好。可以看到物件儲存與 HDFS、JuiceFS 來相比的話,在延遲上是可以差到一個甚至多個數量級的,這個也很好理解,元資料請求對於物件儲存來說本身就是比較大的開銷。反過來看 JuiceFS 和 HDFS 的話,其實是可以做到旗鼓相當的效能表現的。

另一個對比是元資料請求吞吐,越大越好,在某些場景下 JuiceFS 甚至可以做到相較 HDFS 有更好的效能表現,而物件儲存則會相差很多。

JuiceFS 與 Lakehouse

通過觀察 Lakehouse 的特徵,我們首先發現 Lakehouse 對於檔案系統的依賴依然是存在的,如上文提到的 List 效能、原子重新命名、併發寫、強一致性等。其次,物件儲存在使用上是有一些限制的,比如物件儲存基於 key 字首的請求限制,也包括物件儲存的 API 請求是有成本的,特別是在大資料場景,API 請求成本還是蠻高的。最後就是快取加速對於效能的影響。

Lakehouse 對檔案系統的依賴

首先我們看一下 Lakehouse 對於檔案系統的依賴。這裡可以看下面這個表格,這個表格是直接從 Hudi 的官方文件裡面摘抄過來的,Hudi 社群之前統計過直接用物件儲存,並根據不同的檔案規模或者檔案數來做 List 的效能比較。

可以看到從 100 到 100K,隨著檔案數的增多,整個物件儲存 List 的開銷是逐步增大的,到後面已經變成了線性增長。所以在管理大量檔案或者資料時,List 的效能開銷無法忽視。

反過來看 HDFS 或 JuiceFS 這類有獨立的元資料管理的檔案系統,List 請求的開銷其實是非常小的,可以做到毫秒級甚至更快,微秒級都是有可能的。正因為整個檔案系統元資料管理對 List 非常友好,在一個很短的時間內就可以完成整個目錄的 List。還有更多檔案系統獨有的特性,比如原子重新命名、併發寫、強一致性等都是非常關鍵的特性。

Specifically, Delta Lake relies on the following when interacting with storage systems:

  • • Atomic visibility: There must a way for a file to visible in its entirety or not visible at all.

  • • Mutual exclusion: Only one writer must be able to create (or rename) a file at the final destination.

  • • Consistent listing: Once a file has been written in a directory, all future listings for that directory must return that file.

上面這段話是從 Delta Lake 的官方文件上摘抄過來的,在這裡就著重提到了對 Delta Lake 來說它依賴底層的儲存系統需要具備的幾個特性,比如說原子性,其中就包括併發寫、原子重新命名等,然後是一致性的 Listing,這是對於檔案系統強一致性的要求。同樣的,以上這些特性對於不管是 Hudi 或者 Iceberg 來說也都有類似的需求。所以對於檔案系統的特性需求,在 Lakehouse 的元件上都屬於一個隱性的,或者說最基本的依賴,如果物件儲存或其它系統滿足不了某些特性的話就會帶來一些限制。比如說 Delta Lake 在用 S3 的時候,雖然可以併發讀資料,但是無法支援併發寫,只能在單個 Spark driver 裡寫資料來保證事務。

物件儲存的 API 請求限制和成本

提到物件儲存的 API 請求限制和成本的話,這裡我們以 S3 為例,在 AWS 官方文件上其實也已經明確告知使用者,針對每個 prefix(這裡 prefix 的定義就是儲存到 S3 上的每一個物件的 key 的字首)的 GET 請求最大 QPS 是 5500,PUT 請求的最大 QPS 是 3500,對於常規應用而言這個請求限制其實是沒問題的,但是對於大資料場景來說,QPS 限制就會影響整體計算任務的效能甚至是穩定性了。對此 Iceberg 提出了一個優化方法是在 key 的最前面加上一個隨機的雜湊值,目的就是為了分散請求的 prefix,使其不會那麼快地觸碰到針對單個 prefix 的 QPS 限制。

對於 JuiceFS 來說,設計上已經天然具備分散請求 prefix 的理念。因為所有的檔案資料最終上傳到物件儲存的時候,都會被切分成 4MB 的 block 。每個 block 在物件儲存上的 key 其實是一個多級的 prefix 來構成,它不是一個單級的目錄結構。比如說 0/1/123_0_1024 這個 key,是根據每個 block 的 ID 做了兩級 prefix ,然後不同的 block 會分散到不同的 prefix 裡面來。

然後對於同一個檔案來說,如果它是個大檔案的話,它的所有 block 也是分佈在不同的 prefix 裡面的。所以雖然看起來是訪問同一個檔案,但是對於物件儲存來說你訪問的是不同的 prefix,所以這也是 JuiceFS 給檔案分 block 的好處,也是對於物件儲存 API 請求限制的一個優化設計。

其次在物件儲存請求成本上,就 JuiceFS 而言,對於物件儲存 API 的依賴其實非常少,只有 GetObjectPutObjectDeleteObject 這三個 API,剩下的所有 API 都不依賴。所以接入 JuiceFS 資料儲存的儲存系統,只需要提供這三個 API 就夠了,所有的元資料請求都不會經過物件儲存,這部分的 API 請求成本就省掉了。

剛剛提到這三個 API 其實主要是用來讀寫或者刪除資料用的,其中比如 GetObject 是可以通過後面會提到的「快取加速」做進一步的優化,JuiceFS 會自動地把頻繁訪問的資料快取到本地,這樣能夠大幅減少熱資料對於物件儲存 API 請求的依賴。相比直接訪問物件儲存, API 請求成本會降低很多。

快取加速

第三點就是剛剛提到的快取加速,這裡我們拿一個 benchmark 為例,這個 benchmark 使用業界最常見的 TPC-DS 資料集,計算引擎用的是 Presto,資料採用了兩種格式,分別是 ORC 和 Parquet。

可以看到在快取充分預熱的情況下,JuiceFS 的整體效能表現是可以做到與 HDFS 相當的,所以這也是快取加速能夠體現的一些優勢,特別是在存算分離的架構下。

JuiceFS 與資料湖生態

首先 JuiceFS 社群給 Hudi 貢獻了一個 PR 可以在 Hudi 內原生支援 JuiceFS ,這個特性已經在 Hudi v0.10.0 及以上版本支援。具體使用方法可以參考 Hudi 的官方文件。這裡只是拿 Hudi 舉了個例子,其實用 Iceberg、Delta Lake 結合 JuiceFS 也是類似的,JuiceFS 本身已經提供了 HDFS 完全相容的 API,任何使用 HDFS 的地方都可以直接替換為 JuiceFS。

另外 JuiceFS 跟 AI 社群一個比較流行的開源元件 Fluid 也有一些結合。Fluid 是一個開源的以 Kubernetes 環境為主的資料集編排以及訪問加速的元件。目前它主要用在 AI 的場景,但是其實整個 Fluid 社群也想要跟大資料場景做一些結合。Fluid 主要由阿里雲的團隊以及南京大學的一些團隊來維護和開發,它也是 CNCF 裡的一個沙盒專案。

JuiceFS 社群和雲知聲團隊一起給 Fluid 社群貢獻了一個 PR,把 JuiceFS 作為一個 runtime 整合到 Fluid 中。如果用 Fluid 來做 AI 模型訓練,就可以直接原生地使用 JuiceFS 作為其中的一個後端儲存或者說加速元件,幫助你更快地在 Kubernetes 裡把模型訓練任務跑起來。大家有興趣的話,可以檢視 Fluid 的官方文件瞭解一下。

以上就是我今天的分享,感謝大家!

JuiceFS 社群故事徵集ing!

首屆 JuiceFS 社群徵文活動正在進行中 。希望通過本次活動彙集更多優質內容,幫助各位開發者、使用者更加便捷、高效地使用 JuiceFS,更好地融入開源社群, 點此快來投稿吧! FILCO 鍵盤、冰墩墩、社群周邊等你拿~

:point_down: 掃碼加群 :point_down:

:point_down: 關注「 Juicedata 」,獲取更多技術乾貨   :point_down: