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

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

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

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

使用 JuiceFS,文件最終會(huì)被拆分成 Chunks、Slices 和 Blocks 存儲(chǔ)在對(duì)象存儲(chǔ)。因此,你會(huì)發(fā)現(xiàn)在對(duì)象存儲(chǔ)平臺(tái)的文件瀏覽器中找不到存入 JuiceFS 的源文件,存儲(chǔ)桶中只有一個(gè) chunks 目錄和一堆數(shù)字編號(hào)的目錄和文件。不要驚慌,這正是 JuiceFS 高性能運(yùn)作的秘訣!
補(bǔ)充一下源碼中,每個(gè)blocks的命名規(guī)則定義,也就是最終存儲(chǔ)在對(duì)象存儲(chǔ)系統(tǒng)中的對(duì)象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進(jìn)行分區(qū)存儲(chǔ)的,也就是說(shuō)最終存儲(chǔ)數(shù)據(jù)的bucket可以是多個(gè),這樣有助于提高并發(fā)能力,特別是AWS S3每個(gè)bucket是有TPS性能上限的。JuiceFS文件系統(tǒng)golang抽象接口組成
文件系統(tǒng)定義核心數(shù)據(jù)結(jié)構(gòu)
type?FileSystem?struct?{
????conf???*vfs.Config
????reader?vfs.DataReader
????writer?vfs.DataWriter
????m??????meta.Meta
????logBuffer?chan?string
}
下圖為個(gè)人理解所畫的抽象接口結(jié)構(gòu)圖

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



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

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

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

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

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

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