深度拆解:從分布式文件系統(tǒng)設(shè)計(jì)到HDFS對(duì)比與面試指南)
這次我們來(lái)看 GFSGoogle File System也就是 Google 在 2003 年 SOSP 會(huì)議上公開(kāi)的分布式文件系統(tǒng)設(shè)計(jì)。它是整個(gè)大數(shù)據(jù)生態(tài)繞不開(kāi)的祖師爺級(jí)系統(tǒng)HDFS 的塊、副本、NameNode/DataNode 架構(gòu)幾乎都能在 GFS 論文里找到原型。如果你在準(zhǔn)備系統(tǒng)設(shè)計(jì)面試、做分布式存儲(chǔ)選型或者單純想把“分布式文件系統(tǒng)到底怎么設(shè)計(jì)”這件事搞清楚這篇應(yīng)該能省你不少時(shí)間。文章不會(huì)只梳理概念而是按系統(tǒng)設(shè)計(jì)的方法拆解先給設(shè)計(jì)指標(biāo)再拆架構(gòu)組件然后走讀寫(xiě)流程和一致性模型最后落到故障恢復(fù)、與 HDFS 的對(duì)比、面試回答套路這幾個(gè)方向。整個(gè)過(guò)程保持工程視角不堆名詞。1. 核心能力速覽先給一張 GFS 的總體認(rèn)知表方便后續(xù)閱讀時(shí)對(duì)號(hào)入座。下面這些參數(shù)來(lái)自 GFS 發(fā)表于 2003 年的論文數(shù)據(jù)是 Google 當(dāng)時(shí)的集群環(huán)境和負(fù)載特征注意區(qū)分“論文設(shè)計(jì)目標(biāo)”和“當(dāng)前硬件條件下的實(shí)測(cè)結(jié)論”。能力項(xiàng)說(shuō)明系統(tǒng)定位大規(guī)模分布式文件系統(tǒng)面向海量數(shù)據(jù)順序讀、追加寫(xiě)場(chǎng)景發(fā)布時(shí)間2003 年 SOSP 論文公開(kāi)核心解決場(chǎng)景MapReduce、BigTable 等海量數(shù)據(jù)批處理任務(wù)塊Chunk大小64MB遠(yuǎn)大于傳統(tǒng)文件系統(tǒng)的 4KB 級(jí)塊默認(rèn)副本數(shù)3 個(gè)可通過(guò)配置調(diào)整節(jié)點(diǎn)角色Master、ChunkServer、Client一致性模型弱一致性追加寫(xiě)保證“至少一次”可能出現(xiàn)重復(fù)單點(diǎn)問(wèn)題Master 是單點(diǎn)靠操作日志 Checkpoint 恢復(fù)故障恢復(fù)ChunkServer 心跳超時(shí)后重新復(fù)制副本寫(xiě)入模型數(shù)據(jù)流與控制流分離主副本鏈?zhǔn)睫D(zhuǎn)發(fā)與 HDFS 關(guān)系HDFS 是 GFS 設(shè)計(jì)的開(kāi)源簡(jiǎn)化版本適合場(chǎng)景超大文件、追加寫(xiě)、批量讀、粗粒度容錯(cuò)不適合場(chǎng)景小文件密集、低延遲隨機(jī)寫(xiě)、強(qiáng)一致事務(wù)從這張表能直接讀出 GFS 的哲學(xué)它不為通用文件系統(tǒng)設(shè)計(jì)而是為搜索引擎爬取數(shù)據(jù)、網(wǎng)頁(yè)索引、MapReduce 中間結(jié)果這種“一次寫(xiě)多次讀、按批處理”的負(fù)載設(shè)計(jì)。所有核心決策都是從這套負(fù)載特征推出來(lái)的。2. 適用場(chǎng)景與使用邊界學(xué)習(xí)系統(tǒng)設(shè)計(jì)最難的不是記住組件而是理解“為什么這樣設(shè)計(jì)”。GFS 的整個(gè)設(shè)計(jì)幾乎都是圍繞它的目標(biāo)負(fù)載展開(kāi)的。2.1 適合解決什么問(wèn)題GFS 要解決的核心問(wèn)題有三類。第一海量數(shù)據(jù)存儲(chǔ)。Google 當(dāng)時(shí)要存儲(chǔ)全網(wǎng)的網(wǎng)頁(yè)快照、URL 列表、反向索引等數(shù)據(jù)單臺(tái)服務(wù)器的磁盤(pán)放不下必須橫向擴(kuò)展到上千臺(tái)機(jī)器。第二廉價(jià)硬件上的高可靠性。Google 當(dāng)時(shí)用的是大量普通服務(wù)器磁盤(pán)損壞、節(jié)點(diǎn)宕機(jī)是常態(tài)。GFS 必須把故障當(dāng)成正常狀態(tài)來(lái)對(duì)待而不是異常。第三大文件的高吞吐。搜索引擎的索引文件動(dòng)輒幾個(gè) GB 甚至更大文件系統(tǒng)的吞吐能力比隨機(jī)訪問(wèn)延遲更重要。2.2 為什么不適合通用場(chǎng)景反過(guò)來(lái)看GFS 的很多設(shè)計(jì)放在通用文件系統(tǒng)里是反常識(shí)的。64MB 的塊大小對(duì)小文件極不友好。一個(gè) 1KB 的文件也至少要占一個(gè) Chunk 的元數(shù)據(jù)位置上億個(gè)小文件會(huì)直接壓爆 Master 內(nèi)存。弱一致模型不適合需要強(qiáng)一致性的數(shù)據(jù)庫(kù)、交易類應(yīng)用。隨機(jī)寫(xiě)效率差整個(gè)設(shè)計(jì)明顯偏向追加寫(xiě)。POSIX 兼容性幾乎沒(méi)有普通 Linux 程序沒(méi)法直接把 GFS 掛載當(dāng)作 NFS 使用。所以在系統(tǒng)設(shè)計(jì)面試?yán)锶绻}目是“設(shè)計(jì)一個(gè)分布式文件系統(tǒng)”第一步不是抄 GFS而是先定義你的負(fù)載是什么。如果是大量小文件、需要 POSIX 兼容那么 GFS 不是最優(yōu)答案可能要往 JuiceFS、GlusterFS、CephFS 的方向思考。3. GFS 整體架構(gòu)與核心組件GFS 集群由三類角色組成Master、ChunkServer、Client。3.1 Master元數(shù)據(jù)管理中心Master 是集群的“大腦”但它不存文件數(shù)據(jù)只保存三類元數(shù)據(jù)文件命名空間文件名到 Chunk 索引的映射。文件到 Chunk 的映射一個(gè)文件由哪些 Chunk 組成。Chunk 副本的位置信息每個(gè) Chunk 存放在哪些 ChunkServer 上。前兩類元數(shù)據(jù)會(huì)持久化到操作日志Operation Log中第三類副本位置信息不需要持久化Master 啟動(dòng)后通過(guò) ChunkServer 心跳上報(bào)來(lái)構(gòu)建。Master 還負(fù)責(zé)整個(gè)集群的調(diào)度Chunk 副本創(chuàng)建、刪除、遷移、復(fù)制以及租約Lease的管理。所有元數(shù)據(jù)操作都需要經(jīng)過(guò) MasterMaster 因此成為整個(gè)系統(tǒng)最關(guān)鍵的瓶頸點(diǎn)。從論文數(shù)據(jù)看Master 用 64 字節(jié)左右的元數(shù)據(jù)管理一個(gè) Chunk生產(chǎn)環(huán)境中會(huì)進(jìn)一步壓縮。這里不寫(xiě)死數(shù)字要記住的結(jié)論是GFS 的文件數(shù)量上限受 Master 內(nèi)存大小限制這也是它不適合海量小文件的直接原因。3.2 ChunkServer真正存數(shù)據(jù)的地方ChunkServer 負(fù)責(zé)實(shí)際的數(shù)據(jù)存儲(chǔ)。每個(gè) Chunk 在 ChunkServer 上以普通 Linux 文件的形式存在磁盤(pán)可能碎片化但 GFS 不要求 ChunkServer 做復(fù)雜的磁盤(pán)管理。每個(gè) Chunk 默認(rèn)保存 3 個(gè)副本分布在不同的機(jī)架或機(jī)器上。這樣設(shè)計(jì)有兩個(gè)目的一是防止單機(jī)故障導(dǎo)致數(shù)據(jù)丟失二是讀請(qǐng)求可以分散到多個(gè) ChunkServer提升讀取吞吐。ChunkServer 之間會(huì)通過(guò)心跳把自身狀態(tài)上報(bào)給 Master也處理 Client 發(fā)來(lái)的讀寫(xiě)請(qǐng)求。3.3 Client文件訪問(wèn)入口Client 是 GFS 對(duì)應(yīng)用層暴露的 API 接口提供創(chuàng)建文件、刪除文件、打開(kāi)文件、讀文件、寫(xiě)文件等操作。它不是 POSIX 接口而是一套自定義的 GFS API。GFS 的 Client 有一個(gè)重要設(shè)計(jì)它會(huì)緩存 Chunk 的位置信息。一個(gè) Client 在第一次讀取某個(gè) Chunk 時(shí)向 Master 請(qǐng)求位置之后就在本地緩存不用每次讀都打一次 Master。這樣可以大幅降低 Master 壓力但緩存的副作用是位置信息可能過(guò)期所以 Client 需要定期重新請(qǐng)求。3.4 命名空間鎖GFS 的命名空間操作也采用鎖機(jī)制。每個(gè)文件或目錄上可以加讀鎖或?qū)戞i。多個(gè)操作需要獲取鎖后才能執(zhí)行例如創(chuàng)建文件要在父目錄上持有讀鎖、在目標(biāo)文件名上持有寫(xiě)鎖。因?yàn)殒i粒度細(xì)GFS 的并發(fā)目錄操作性能比傳統(tǒng)文件系統(tǒng)高得多。這個(gè)設(shè)計(jì)后面在面試?yán)锟梢援?dāng)作亮點(diǎn)回答“GFS 如何保證目錄并發(fā)操作安全”。4. GFS 讀寫(xiě)流程拆解這一部分是系統(tǒng)設(shè)計(jì)面試最容易考的細(xì)節(jié)。光記住“Client 連 Master 拿元數(shù)據(jù)然后連 ChunkServer 讀寫(xiě)數(shù)據(jù)”不夠要能把每個(gè)步驟說(shuō)清楚。4.1 寫(xiě)流程普通 WriteGFS 的寫(xiě)流程可以拆成下面幾步Client 向 Master 請(qǐng)求目標(biāo) Chunk 的持有租約的 ChunkServer 位置。Master 返回主副本 ChunkServer 和其他副本 ChunkServer 的地址。Client 把數(shù)據(jù)推送到離自己最近的副本 ChunkServer數(shù)據(jù)流。收到數(shù)據(jù)的 ChunkServer 把數(shù)據(jù)繼續(xù)推送到鏈路中的下一個(gè)副本 ChunkServer依此類推直到所有副本都收到數(shù)據(jù)。所有副本確認(rèn)收到數(shù)據(jù)后Client 向主副本 ChunkServer 發(fā)送寫(xiě)請(qǐng)求控制流。主副本 ChunkServer 為這次寫(xiě)分配一個(gè)全局唯一的序列號(hào)按序?qū)懭氩褜?xiě)請(qǐng)求發(fā)給其他副本 ChunkServer。所有副本完成寫(xiě)入后主副本向 Client 返回成功響應(yīng)。Client 收到響應(yīng)后如果中間有一步失敗就會(huì)重試整個(gè)流程。這個(gè)流程里有幾個(gè)關(guān)鍵點(diǎn)值得展開(kāi)。關(guān)鍵點(diǎn)一數(shù)據(jù)流與控制流分離。控制路徑是 Client - 主副本 - 其他副本數(shù)據(jù)路徑是 Client - 最近的副本 - 鏈?zhǔn)睫D(zhuǎn)發(fā)到其他副本。這樣設(shè)計(jì)的目的很直接數(shù)據(jù)沿著最短路徑傳輸減少跨機(jī)架流量控制路徑則保證寫(xiě)入順序一致。關(guān)鍵點(diǎn)二主副本的寫(xiě)入順序。所有并發(fā)寫(xiě)請(qǐng)求都由主副本分配序列號(hào)保證多個(gè) Client 并發(fā)寫(xiě)同一個(gè) Chunk 時(shí)所有副本以相同順序接收數(shù)據(jù)避免副本間數(shù)據(jù)不一致。關(guān)鍵點(diǎn)三寫(xiě)入失敗的處理。GFS 的寫(xiě)入不是原子的。如果一個(gè)寫(xiě)操作在部分副本成功、部分副本失敗GFS 會(huì)返回錯(cuò)誤給應(yīng)用層應(yīng)用層需要自己決定是否重試。這部分體現(xiàn)了 GFS 的設(shè)計(jì)哲學(xué)把復(fù)雜性交給應(yīng)用層而不是隱藏在文件系統(tǒng)內(nèi)部。4.2 追加寫(xiě)流程Record Append追加寫(xiě)是 GFS 的重要特性也是面試的高頻考點(diǎn)。MapReduce 這類系統(tǒng)經(jīng)常需要把多個(gè)結(jié)果追加到同一個(gè)文件如果使用普通 Write多個(gè) Client 并發(fā)寫(xiě)會(huì)導(dǎo)致數(shù)據(jù)交錯(cuò)不可接受。GFS 的 Record Append 語(yǔ)義如下多個(gè) Client 可以并發(fā)對(duì)同一個(gè)文件追加數(shù)據(jù)。GFS 保證每條記錄至少被寫(xiě)入一次at-least-once但不保證每條記錄只寫(xiě)入一次。如果某次追加在部分副本上成功、部分失敗GFS 會(huì)返回錯(cuò)誤應(yīng)用層需要檢查該區(qū)域是否出現(xiàn)重復(fù)記錄。每條追加的記錄在文件中是原子性的即讀取方不會(huì)看到半條記錄。這種“至少一次”模型非常適合 MapReduce 的中間結(jié)果寫(xiě)入哪怕有重復(fù)記錄應(yīng)用層也能通過(guò)唯一標(biāo)識(shí)符去重。4.3 讀流程讀流程相對(duì)簡(jiǎn)單Client 向 Master 發(fā)送讀請(qǐng)求攜帶文件名和偏移量。Master 返回對(duì)應(yīng)的 Chunk 句柄和副本位置。Client 選擇一個(gè)副本位置通常是最近的緩存到本地。Client 向 ChunkServer 發(fā)送讀請(qǐng)求指定 Chunk 句柄和字節(jié)范圍。ChunkServer 返回?cái)?shù)據(jù)。如果 ChunkServer 返回?cái)?shù)據(jù)校驗(yàn)失敗比如磁盤(pán)壞道Client 會(huì)換一個(gè)副本讀取并把壞副本情況上報(bào)給 Master。5. GFS 一致性模型與故障恢復(fù)GFS 的一致性模型是它最容易被誤讀的部分。很多人都知道 GFS“弱一致”但當(dāng)被問(wèn)“具體弱在哪里”“和數(shù)據(jù)一致性面試題里的最終一致性有什么區(qū)別”時(shí)往往答不清楚。5.1 一致性定義GFS 論文定義了四種狀態(tài)一致Consistent所有客戶端看到相同的數(shù)據(jù)無(wú)論它們?cè)L問(wèn)的是哪個(gè)副本。已定義Defined文件區(qū)域是一致的且客戶端能夠看到寫(xiě)入操作寫(xiě)入的完整數(shù)據(jù)。已定義但中間結(jié)果交錯(cuò)并發(fā)寫(xiě)交織在一起每條記錄都是完整的但先后順序不確定。不一致Inconsistent不同客戶端可能看到不同的副本內(nèi)容。對(duì)于普通寫(xiě)操作如果成功寫(xiě)入的區(qū)域是已定義的如果并發(fā)寫(xiě)區(qū)域是已定義的但數(shù)據(jù)可能交錯(cuò)如果有部分失敗區(qū)域可能不一致。對(duì)于追加寫(xiě)操作只要追加成功區(qū)域就是已定義的但可能存在重復(fù)記錄。GFS 不處理重復(fù)交給應(yīng)用層判斷。5.2 Master 的故障恢復(fù)Master 單點(diǎn)故障是 GFS 最明顯的弱點(diǎn)。它通過(guò)兩個(gè)方面緩解操作日志Operation LogMaster 的每次元數(shù)據(jù)操作都會(huì)追加到日志中寫(xiě)日志成功后才返回客戶端成功。Checkpoint當(dāng)操作日志達(dá)到一定大小Master 生成 Checkpoint 快照之后從 Checkpoint 加載狀態(tài)再重放后續(xù)日志。GFS 論文里提到Master 恢復(fù)時(shí)還有影子 MasterShadow Master機(jī)制提供只讀訪問(wèn)避免恢復(fù)期間整個(gè)系統(tǒng)不可用?;謴?fù)時(shí)間取決于日志重放速度但遠(yuǎn)比重新掃描所有 ChunkServer 要快。5.3 ChunkServer 故障與副本恢復(fù)Master 通過(guò)周期心跳監(jiān)控 ChunkServer。如果某個(gè) ChunkServer 心跳超時(shí)Master 會(huì)將該節(jié)點(diǎn)標(biāo)記為不可用并為它負(fù)責(zé)的 Chunk 在每個(gè)副本數(shù)低于閾值的文件上重新創(chuàng)建副本。新副本的創(chuàng)建優(yōu)先從最后一個(gè)活副本復(fù)制因?yàn)樽詈笠粋€(gè)副本的數(shù)據(jù)往往最新。復(fù)制完成后Master 更新元數(shù)據(jù)中的副本位置信息。5.4 租約機(jī)制租約是 GFS 控制并發(fā)寫(xiě)順序的核心機(jī)制。主副本 ChunkServer 擁有一個(gè)租約租約有效期默認(rèn) 60 秒。租約內(nèi)該 ChunkServer 作為主副本接受寫(xiě)請(qǐng)求分配寫(xiě)入順序。租約到期后Master 可以重新分配主副本給其他 ChunkServer。如果主副本宕機(jī)或與 Master 失聯(lián)租約會(huì)在超時(shí)后自動(dòng)到期新的主副本會(huì)被重新選舉。租約的核心作用就是解決“主副本掛了誰(shuí)來(lái)做主”的問(wèn)題。沒(méi)有租約兩個(gè) ChunkServer 會(huì)同時(shí)認(rèn)為自己有寫(xiě)權(quán)限數(shù)據(jù)一致性就會(huì)出問(wèn)題。5.5 垃圾回收GFS 的刪除操作不立即釋放空間。刪除文件時(shí)Master 只是把文件名標(biāo)記為隱藏并在操作日志中記錄然后延遲一段時(shí)間通常是 3 天后才真正刪除物理數(shù)據(jù)。這樣做的好處是如果誤刪文件可以快速恢復(fù)也避免了刪除過(guò)程中的復(fù)雜狀態(tài)管理。壞處是磁盤(pán)空間回收有延遲對(duì)于空間緊張的場(chǎng)景不友好。6. 系統(tǒng)設(shè)計(jì)亮點(diǎn)與潛在問(wèn)題GFS 能在系統(tǒng)設(shè)計(jì)面試?yán)锉环磸?fù)提及是因?yàn)樗w現(xiàn)了大量可復(fù)用的設(shè)計(jì)思想。6.1 值得借鑒的設(shè)計(jì)亮點(diǎn)大塊設(shè)計(jì)降低元數(shù)據(jù)壓力。64MB 的 Chunk 讓文件被切分的數(shù)量大大減少M(fèi)aster 需要維護(hù)的元數(shù)據(jù)項(xiàng)也隨之減少。同時(shí)一個(gè) Chunk 可以在一個(gè) ChunkServer 上連續(xù)存儲(chǔ)有利于順序讀寫(xiě)。租約避免集中式鎖服務(wù)。GFS 沒(méi)有引入 Paxos 或 Raft 來(lái)處理 Master 選主和分布式鎖而是用租約把寫(xiě)控制權(quán)下放到單個(gè) ChunkServer。這讓系統(tǒng)在早期避免了復(fù)雜的分布式共識(shí)問(wèn)題代價(jià)是 Master 成為單點(diǎn)。數(shù)據(jù)流與控制流分離。寫(xiě)入數(shù)據(jù)沿著復(fù)制鏈傳輸控制流獨(dú)立走另一條路徑。這個(gè)設(shè)計(jì)在 HDFS 中也被繼承是理解分布式存儲(chǔ)寫(xiě)入路徑的重要概念。追加寫(xiě)模型契合批處理負(fù)載。多個(gè) Consumer 并發(fā)寫(xiě)入同一個(gè)日志文件是 MapReduce 中最常見(jiàn)的寫(xiě)模式。GFS 為這種模式設(shè)計(jì)的 Record Append 語(yǔ)義比 POSIX 的 write 語(yǔ)義更實(shí)用。寫(xiě)后校驗(yàn)和保證數(shù)據(jù)完整性。每個(gè) ChunkServer 會(huì)為 Chunk 中的每個(gè) 64KB 小分塊計(jì)算校驗(yàn)和。讀取時(shí)校驗(yàn)失敗會(huì)嘗試其他副本防止靜默數(shù)據(jù)損壞。寫(xiě)時(shí)復(fù)制快照。GFS 的快照用寫(xiě)時(shí)復(fù)制Copy-on-Write實(shí)現(xiàn)創(chuàng)建快照時(shí)不需要復(fù)制數(shù)據(jù)只有在修改某個(gè) Chunk 時(shí)才復(fù)制成本極低。6.2 需要避開(kāi)的坑Master 單點(diǎn)瓶頸。論文時(shí)代的 GFS 沒(méi)有真正解決 Master 單點(diǎn)問(wèn)題主要依賴恢復(fù)機(jī)制而非可用性機(jī)制。后來(lái)的 Colossus 才解決了元數(shù)據(jù)水平擴(kuò)展問(wèn)題。系統(tǒng)設(shè)計(jì)時(shí)如果你的集群規(guī)模超過(guò)幾萬(wàn)臺(tái)單 Master 設(shè)計(jì)基本不可行。元數(shù)據(jù)全內(nèi)存。所有元數(shù)據(jù)都放在 Master 內(nèi)存中文件數(shù)量被內(nèi)存上限鎖死。設(shè)計(jì)時(shí)如果預(yù)判小文件數(shù)量巨大要么用內(nèi)存更大的機(jī)器要么引入元數(shù)據(jù)分片。追加寫(xiě)重復(fù)。“至少一次”意味著數(shù)據(jù)可能重復(fù)寫(xiě)入這對(duì)需要精確一次的流式處理場(chǎng)景不夠友好。應(yīng)用層必須做冪等處理。隨機(jī)寫(xiě)性能差。GFS 的寫(xiě)路徑是發(fā)給主副本再鏈?zhǔn)綇?fù)制隨機(jī)寫(xiě)場(chǎng)景會(huì)產(chǎn)生大量隨機(jī) IO性能會(huì)很難看。7. 與 HDFS 的對(duì)比GFS 論文不開(kāi)放源碼但 HDFS 在設(shè)計(jì)上大量借鑒了 GFS 的思想理解兩者的差異能幫助你更清楚地看到 GFS 的本質(zhì)。對(duì)比項(xiàng)GFSHDFS論文/源碼論文公開(kāi)代碼未開(kāi)源開(kāi)源實(shí)現(xiàn)塊大小64MB默認(rèn) 128MB主節(jié)點(diǎn)MasterNameNode數(shù)據(jù)節(jié)點(diǎn)ChunkServerDataNode寫(xiě)入模型隨機(jī)寫(xiě) 追加寫(xiě)追加為主早期不支持隨機(jī)寫(xiě)一致性弱一致追加至少一次同 GFS 思路弱一致高可用恢復(fù)機(jī)制 Shadow Master2NN / QJM 高可用方案典型應(yīng)用MapReduce、BigTableMapReduce、Spark、HivePOSIX 兼容否否HDFS 最大的改進(jìn)是引入了高可用方案Active NameNode 和 Standby NameNode 通過(guò) JournalNode 共享編輯日志主節(jié)點(diǎn)故障時(shí)可以進(jìn)行自動(dòng)切換。而 GFS 論文時(shí)代的 Master 恢復(fù)依賴 Shadow Master不能自動(dòng)切換為可寫(xiě)狀態(tài)。另一個(gè)差異是 HDFS 早期不支持隨機(jī)寫(xiě)只支持追加寫(xiě)。從這個(gè)角度看HDFS 對(duì) GFS 的任務(wù)負(fù)載理解得更徹底刪掉不常用的能力保留最核心的批處理路徑。8. 面試與系統(tǒng)設(shè)計(jì)題怎么答GFS 是系統(tǒng)設(shè)計(jì)面試中“分布式文件系統(tǒng)”“分布式存儲(chǔ)”類題目的原型。這里給出一套直接可用的答題框架。8.1 問(wèn)題設(shè)計(jì)一個(gè)分布式文件系統(tǒng)如果面試官讓你設(shè)計(jì)分布式文件系統(tǒng)可以按照下面的順序組織回答。第一步定義需求。先問(wèn)清楚數(shù)據(jù)規(guī)模、文件大小分布、讀寫(xiě)比例、一致性要求、容錯(cuò)要求。GFS 就是先確定了“大文件、順序讀、追加寫(xiě)、弱一致可接受”這套約束才推導(dǎo)出后續(xù)的所有設(shè)計(jì)。第二步定架構(gòu)。采用 Master-Slave 結(jié)構(gòu)Master 管元數(shù)據(jù)數(shù)據(jù)節(jié)點(diǎn)管數(shù)據(jù)。點(diǎn)出 Master 單點(diǎn)是后續(xù)要解決的問(wèn)題。第三步拆數(shù)據(jù)單元。設(shè)置大塊64MB 或 128MB三個(gè)副本分布在多機(jī)房/多機(jī)架。這一步能體現(xiàn)你理解元數(shù)據(jù)規(guī)模和數(shù)據(jù)可靠性的權(quán)衡。第四步設(shè)計(jì)讀寫(xiě)流程。寫(xiě)流程要講數(shù)據(jù)流與控制流分離、主副本鏈?zhǔn)綇?fù)制讀流程要講按副本就近讀取、位置緩存。第五步處理故障。Master 故障用操作日志 Checkpoint 恢復(fù)ChunkServer 故障用副本重復(fù)制主節(jié)點(diǎn)故障可以用租約 新主選舉。第六步說(shuō)清楚一致性模型。明確系統(tǒng)是弱一致的追加寫(xiě)至少一次應(yīng)用層需要處理重復(fù)數(shù)據(jù)。8.2 問(wèn)題為什么 GFS 不采用 POSIX 接口這個(gè)問(wèn)題考察的是“你是否真的理解 GFS 面向批處理的本質(zhì)”。GFS 的目標(biāo)負(fù)載是 MapReduce 和 BigTable它們不需要 POSIX 接口的語(yǔ)義不需要目錄的 POSIX 權(quán)限控制不需要文件鎖不需要 mmap。換成自定義 API 后GFS 可以對(duì) Chunk 做更細(xì)粒度的位置感知和調(diào)度這是 POSIX 接口做不到的。8.3 問(wèn)題Master 掛了怎么辦這是考系統(tǒng)設(shè)計(jì)基本功的問(wèn)題?;卮鸱殖扇糠忠皇菙?shù)據(jù)不丟因?yàn)閿?shù)據(jù)都在 ChunkServer 上Master 只存元數(shù)據(jù)二是元數(shù)據(jù)能恢復(fù)通過(guò)操作日志和 Checkpoint 重放三是恢復(fù)期間服務(wù)降級(jí)可以用 Shadow Master 提供只讀服務(wù)。引申一步再補(bǔ)一句真正解決單點(diǎn)問(wèn)題的方案是把 Master 設(shè)計(jì)成主備模式并用共識(shí)算法選主或者做元數(shù)據(jù)分片Google 后來(lái)在 Colossus 中采用后者。9. 學(xué)習(xí) GFS 常見(jiàn)誤區(qū)與排查思路這里不是運(yùn)維排障而是學(xué)習(xí)路徑上的“排雷”。下表列出大多數(shù)人理解 GFS 時(shí)容易踩的坑以及對(duì)應(yīng)的修正方法。誤區(qū)現(xiàn)象錯(cuò)誤理解正確理解如何驗(yàn)證以為 GFS 支持強(qiáng)一致寫(xiě)入寫(xiě)入成功后所有副本立刻相同寫(xiě)成功只代表主副本接收成功副本間弱一致閱讀論文一致性定義段落以為 GFS 和 HDFS 完全等價(jià)兩者架構(gòu)完全相同核心思想相同HDFS 簡(jiǎn)化和演進(jìn)對(duì)比論文與 HDFS 文檔以為 Chunk 是“文件分塊后存多份”Chunk 就是文件邏輯分塊Chunk 是獨(dú)立的、可移動(dòng)的存儲(chǔ)單位有獨(dú)立句柄分析 Chunk 句柄和副本管理流程以為追加寫(xiě)保證不重復(fù)Record Append 寫(xiě)入一次后不再出現(xiàn)至少一次語(yǔ)義可能重復(fù)看論文中重復(fù)和原子性說(shuō)明以為 Master 保存所有副本位置Master 持久化所有副本位置副本位置信息不持久化靠心跳重建閱讀 Master 元數(shù)據(jù)說(shuō)明以為租約只用于 Master 選主租約只是主節(jié)點(diǎn)授權(quán)租約用于 ChunkServer 寫(xiě)授權(quán)避免并發(fā)寫(xiě)沖突分析寫(xiě)流程中的租約獲取如果在學(xué)習(xí)時(shí)發(fā)現(xiàn)“這個(gè)設(shè)計(jì)不合理”先別急著否定。GFS 的價(jià)值恰恰在于你能看明白它的每一個(gè)不合理之處才說(shuō)明你理解了它背后的約束條件。比如“延遲刪除”看起來(lái)浪費(fèi)磁盤(pán)但在分布式系統(tǒng)里延遲刪除能換來(lái)恢復(fù)能力和更簡(jiǎn)單的狀態(tài)機(jī)。10. 最佳實(shí)踐把 GFS 思想用在工程里學(xué)習(xí) GFS 不是為了讓面試官滿意更重要的是把這些設(shè)計(jì)思想帶到實(shí)際系統(tǒng)設(shè)計(jì)里。下面幾條是我在實(shí)際工程中最常借鑒的。第一先定義負(fù)載再選架構(gòu)。不是所有文件系統(tǒng)都需要 POSIX也不是所有存儲(chǔ)都需要強(qiáng)一致。如果你的業(yè)務(wù)是日志收集、事件流、批量 ETLGFS 風(fēng)格的大文件追加寫(xiě)模型能帶來(lái)極高吞吐如果業(yè)務(wù)是電商訂單這類強(qiáng)一致事務(wù)就不適合。第二用大塊降低元數(shù)據(jù)規(guī)模。設(shè)計(jì)分布式對(duì)象存儲(chǔ)時(shí)把對(duì)象切分為較大的分片比如 64MB 或更大減少索引條目數(shù)量這樣元數(shù)據(jù)服務(wù)壓力會(huì)小很多。代價(jià)是空間分配有碎片小對(duì)象會(huì)浪費(fèi)空間需要根據(jù)對(duì)象大小分布來(lái)選擇分片大小。第三寫(xiě)操作必須配操作日志。主節(jié)點(diǎn)元數(shù)據(jù)一旦丟失整個(gè)集群就廢了。操作日志加 Checkpoint 是最簡(jiǎn)單的恢復(fù)方案成本低、實(shí)現(xiàn)簡(jiǎn)單HDFS 的 FsImage EditLog 就是這么做的。第四用租約而不是鎖服務(wù)選主。當(dāng)沒(méi)有強(qiáng)一致性要求時(shí)租約機(jī)制可以用非常簡(jiǎn)單的代碼實(shí)現(xiàn)寫(xiě)權(quán)限控制。如果要求高可用再加主備切換和共識(shí)協(xié)議。第五批量任務(wù)一定要冪等。GFS 的追加寫(xiě)至少一次語(yǔ)義意味著下游處理程序要做好重復(fù)數(shù)據(jù)處理。在實(shí)際工程中凡是依賴分布式文件系統(tǒng)做消息或日志存儲(chǔ)的場(chǎng)景消費(fèi)端都應(yīng)該按唯一 ID 去重。第六做容量規(guī)劃時(shí)給副本留余量。默認(rèn) 3 副本意味著你的物理存儲(chǔ)成本是邏輯數(shù)據(jù)的 3 倍但壞一個(gè)節(jié)點(diǎn)后系統(tǒng)要立刻復(fù)制新副本又會(huì)觸發(fā)更多磁盤(pán) IO。生產(chǎn)環(huán)境建議把磁盤(pán)水位控制在 70% 左右給副本復(fù)制和臨時(shí)文件留空間。11. 總結(jié)與下一步GFS 最值得花時(shí)間研究的點(diǎn)不是它的具體參數(shù)而是它如何從負(fù)載特征推導(dǎo)出架構(gòu)選擇大文件順序讀導(dǎo)致了大塊設(shè)計(jì)可靠性要求導(dǎo)致了 3 副本和自動(dòng)恢復(fù)弱一致性要求導(dǎo)致了簡(jiǎn)潔的寫(xiě)路徑和追加寫(xiě)模型。把這套“先定義問(wèn)題、再選擇方案”的思維方式拿下來(lái)比記住 64MB、3 副本這些數(shù)字重要得多。接下來(lái)可以按兩條線繼續(xù)深挖。第一條線是讀論文原文重點(diǎn)看第 3 章架構(gòu)設(shè)計(jì)、第 4 章交互流程和第 5 章一致性模型論文里的圖對(duì)理解流程非常關(guān)鍵。第二條線是上手 HDFS在一個(gè)小集群上實(shí)際執(zhí)行文件讀寫(xiě)觀察 NameNode 元數(shù)據(jù)變化、DataNode 心跳和副本復(fù)制過(guò)程把 GFS 的抽象概念落到真實(shí)系統(tǒng)上。如果目標(biāo)是應(yīng)對(duì)系統(tǒng)設(shè)計(jì)面試建議把寫(xiě)流程和一致性模型練到“不看筆記能畫(huà)流程圖”的程度。這兩個(gè)點(diǎn)是面試官最常追問(wèn)的細(xì)節(jié)也是區(qū)分“看過(guò)概述”和“真正理解”的分水嶺。后續(xù)還可以對(duì)比閱讀 BigTable 論文和 Chubby 鎖服務(wù)的論文這三篇加起來(lái)基本構(gòu)成了 Google 早期分布式系統(tǒng)的三大支柱也是進(jìn)入分布式系統(tǒng)領(lǐng)域的最佳教材組合。