JuiceFS 源碼閱讀-上
JuiceFS 源碼閱讀-上
最近研究文件系統(tǒng),把近期比較火的JuiceFS代碼翻出來看了一下,研究為啥其性能要比CephFS要好。
背景知識
參考資源:https://mp.weixin.qq.com/s/HvbMxNiVudjNPRgYC8nXyg
FUSE 是一個用來實現(xiàn)用戶態(tài)文件系統(tǒng)的框架,這套 FUSE 框架包含 3 個組件:
內(nèi)核模塊 fuse.ko :用來接收 vfs 傳遞下來的 IO 請求,并且把這個 IO 封裝之后通過管道發(fā)送到用戶態(tài);
用戶態(tài) lib 庫 libfuse :解析內(nèi)核態(tài)轉(zhuǎn)發(fā)出來的協(xié)議包,拆解成常規(guī)的 IO 請求;
mount 工具 fusermount ;
背景:一個用戶態(tài)文件系統(tǒng),掛載點為 /tmp/fuse ,用戶二進制程序文件為 ./hello ;當執(zhí)行 ls -l /tmp/fuse 命令的時候,流程如下:
IO 請求先進內(nèi)核,經(jīng) vfs 傳遞給內(nèi)核 FUSE 文件系統(tǒng)模塊;
內(nèi)核 FUSE 模塊把請求發(fā)給到用戶態(tài),由 ./hello 程序接收并且處理。處理完成之后,響應原路返回;

JuiceFS架構簡介
JuiceFS 由三個部分組成:
JuiceFS 客戶端:協(xié)調(diào)對象存儲和元數(shù)據(jù)存儲引擎,以及 POSIX、Hadoop、Kubernetes、S3 Gateway 等文件系統(tǒng)接口的實現(xiàn);
數(shù)據(jù)存儲:存儲數(shù)據(jù)本身,支持本地磁盤、對象存儲;
元數(shù)據(jù)引擎:存儲數(shù)據(jù)對應的元數(shù)據(jù),支持 Redis、MySQL、SQLite 等多種引擎;

JuiceFS 依靠 Redis 來存儲文件的元數(shù)據(jù)。Redis 是基于內(nèi)存的高性能的鍵值數(shù)據(jù)存儲,非常適合存儲元數(shù)據(jù)。與此同時,所有數(shù)據(jù)將通過 JuiceFS 客戶端存儲到對象存儲中。

任何存入 JuiceFS 的文件都會被拆分成固定大小的?"Chunk",默認的容量上限是 64 MiB。每個 Chunk 由一個或多個?"Slice"?組成,Slice 的長度不固定,取決于文件寫入的方式。每個 Slice 又會被進一步拆分成固定大小的?"Block",默認為 4 MiB。最后,這些 Block 會被存儲到對象存儲。與此同時,JuiceFS 會將每個文件以及它的 Chunks、Slices、Blocks 等元數(shù)據(jù)信息存儲在元數(shù)據(jù)引擎中。

使用 JuiceFS,文件最終會被拆分成 Chunks、Slices 和 Blocks 存儲在對象存儲。因此,你會發(fā)現(xiàn)在對象存儲平臺的文件瀏覽器中找不到存入 JuiceFS 的源文件,存儲桶中只有一個 chunks 目錄和一堆數(shù)字編號的目錄和文件。不要驚慌,這正是 JuiceFS 高性能運作的秘訣!
補充一下源碼中,每個blocks的命名規(guī)則定義,也就是最終存儲在對象存儲系統(tǒng)中的對象key名稱。
func?(c?*rChunk)?key(indx?int)?string?{
????if?c.store.conf.Partitions?>?1?{
????????return?fmt.Sprintf("chunks/%02X/%v/%v_%v_%v",?c.id%256,?c.id/1000/1000,?c.id,?indx,?c.blockSize(indx))
????}
????return?fmt.Sprintf("chunks/%v/%v/%v_%v_%v",?c.id/1000/1000,?c.id/1000,?c.id,?indx,?c.blockSize(indx))
}
從命名規(guī)則里面也能看出,數(shù)據(jù)是支持按partition進行分區(qū)存儲的,也就是說最終存儲數(shù)據(jù)的bucket可以是多個,這樣有助于提高并發(fā)能力,特別是AWS S3每個bucket是有TPS性能上限的。JuiceFS文件系統(tǒng)golang抽象接口組成
文件系統(tǒng)定義核心數(shù)據(jù)結(jié)構
type?FileSystem?struct?{
????conf???*vfs.Config
????reader?vfs.DataReader
????writer?vfs.DataWriter
????m??????meta.Meta
????logBuffer?chan?string
}
下圖為個人理解所畫的抽象接口結(jié)構圖

整個JuiceFS文件系統(tǒng)實現(xiàn)主要拆分為VFS抽象實現(xiàn)和相關的config配置管理兩大部分。
任意文件File操作都涉及到數(shù)據(jù)和元數(shù)據(jù)兩部分內(nèi)容,因此代碼中包含數(shù)據(jù)處理相關的DataReader和DataWriter兩個抽象接口,用來處理數(shù)據(jù)的讀取和寫入兩類請求。而元數(shù)據(jù)抽象出Meta一個數(shù)據(jù)庫相關的接口,基于這個接口目前官方實現(xiàn)了dbMeta也就是兼容SQL相關的元數(shù)據(jù)實現(xiàn),以及redisMeta實現(xiàn)(基于redis)。從性能表現(xiàn)來看,redis比MySQL性能要好3~5倍左右。具體可以參考這個
所有的數(shù)據(jù)讀寫操作都要和本地緩存進行交互(Chunk->Slice->block(page)三個層級進行管理),緩存目前主要實現(xiàn)了基于本地文件系統(tǒng)diskStore和基于內(nèi)存緩存cacheStore(堆空間)兩種類型。數(shù)據(jù)寫入和讀取最終都是由對應的緩存模塊同步到遠程的ObjectSotrage。
config主要負責對本地緩存、元數(shù)據(jù)引擎連接信息等相關的配置。
JuiceFS支持的數(shù)據(jù)列表:
https://github.com/juicedata/juicefs/blob/main/docs/zh_cn/databases_for_metadata.md
JuiceFS元數(shù)據(jù)redis與MySQL性能對比測試:
https://github.com/juicedata/juicefs/blob/main/docs/en/metadata_engines_benchmark.md
下圖是源碼中對ChunkStore的抽象接口定義,通過NewReader和NewWriter生成對應的Reader讀取抽象和Writer抽象,實現(xiàn)數(shù)據(jù)的讀寫相關原子接口。



任何廠家的對象存儲產(chǎn)品,只要實現(xiàn)下面的接口抽象,即可實現(xiàn)與JuiceFS的對接。

數(shù)據(jù)讀取抽象接口
下圖是數(shù)據(jù)讀取抽象接口的繼承組合關系

最終的數(shù)據(jù)讀取關聯(lián)到rChunk這個struct的相關method方法。

數(shù)據(jù)寫入抽象接口
下圖是數(shù)據(jù)寫入抽象接口的繼承組合關系

最終的數(shù)據(jù)讀取關聯(lián)到wChunk這個struct的相關method方法。

小節(jié)
至此,基本理清了JuiceFS的基本構架和核心數(shù)據(jù)結(jié)構,下節(jié)開始對其讀寫具體接口流程進行剖析。
打個小廣告,團隊持續(xù)招聘存儲開發(fā)和運維同學,感興趣的可以看看之前公眾號網(wǎng)易游戲招聘相關內(nèi)容。
