主流分佈式文件系統選型,寫得太好了!

點擊關注公衆號,Java乾貨及時送達

作者:張軻1983
來源:https://www.jianshu.com/p/fc0aa34606ce

一、概述

分佈式文件系統是分佈式領域的一個基礎應用,其中最著名的毫無疑問是 HDFS/GFS。如今該領域已經趨向於成熟,但瞭解它的設計要點和思想,對我們將來面臨類似場景/問題時,具有借鑑意義。

並且,分佈式文件系統並非只有 HDFS/GFS 這一種形態,在它之外,還有其他形態各異、各有千秋的產品形態,對它們的瞭解,也對擴展我們的視野有所俾益。

本文試圖分析和思考,在分佈式文件系統領域,我們要解決哪些問題、有些什麼樣的方案、以及各自的選擇依據。

二、過去的樣子

在幾十年以前,分佈式文件系統就已經出現了,以 Sun 在 1984 年開發的“Network File System (NFS)”爲代表,那時候解決的主要問題,是網絡形態的磁盤,把磁盤從主機中獨立出來。

這樣不僅可以獲得更大的容量,而且還可以隨時切換主機,還可以實現數據共享、備份、容災等,因爲數據是電腦中最重要的資產。

NFS 的數據通信圖如下:

部署在主機上的客戶端,通過 TCP/IP 協議把文件命令轉發到遠程文件 Server 上執行,整個過程對主機用戶透明。

到了互聯網時代,流量和數據快速增長,分佈式文件系統所要解決的主要場景變了,開始需要非常大的磁盤空間,這在磁盤體系上垂直擴容是無法達到的,必須要分佈式,同時分佈式架構下,主機都是可靠性不是非常好的普通服務器,因此容錯、高可用、持久化、伸縮性等指標,就成爲必須要考量的特性。

三、對分佈式文件系統的要求

對一個分佈式文件系統而言,有一些特性是必須要滿足的,否則就無法有競爭力。主要如下:

除此之外,還有些特性是分佈式加分項,具體如下:

  • 支持的空間越大越好;
  • 支持的併發訪問請求越多越好;
  • 性能越快越好;
  • 硬件資源的利用率越高越合理,就越好。

四、架構模型

從業務模型和邏輯架構上,分佈式文件系統需要這幾類組件:

而在部署架構上,有着“中心化”和“無中心化”兩種路線分歧,即是否把“管理組件”作爲分佈式文件系統的中心管理節點。兩種路線都有很優秀的產品,下面分別介紹它們的區別。

1、有中心節點

以 GFS 爲代表,中心節點負責文件定位、維護文件 meta 信息、故障檢測、數據遷移等管理控制的職能,下圖是 GFS 的架構圖:

GFS架構

該圖中GFS master 即爲 GFS 的中心節點,GF chunkserver 爲 GFS 的存儲節點。其操作路徑如下:

  • Client 向中心節點請求“查詢某個文件的某部分數據”;
  • 中心節點返回文件所在的位置 (哪臺 chunkserver 上的哪個文件) 以及字節區間信息;
  • Client 根據中心節點返回的信息,向對應的 chunk server 直接發送數據讀取的請求;
  • chunk server 返回數據。

在這種方案裏,一般中心節點並不參與真正的數據讀寫,而是將文件 meta 信息返回給 Client 之後,即由 Client 與數據節點直接通信。其主要目的是降低中心節點的負載,防止其成爲瓶頸。這種有中心節點的方案,在各種存儲類系統中得到了廣泛應用,因爲中心節點易控制、功能強大。

2、無中心節點

以ceph爲代表,每個節點都是自治的、自管理的,整個 ceph 集羣只包含一類節點,如下圖 (最下層紅色的 RADOS 就是 ceph 定義的“同時包含 meta 數據和文件數據”的節點)。

Ceph架構

無中心化的最大優點是解決了中心節點自身的瓶頸,這也就是 ceph 號稱可以無限向上擴容的原因。但由 Client 直接和 Server 通信,那麼 Client 必須要知道,當對某個文件進行操作時,它該訪問集羣中的哪個節點。ceph 提供了一個很強大的原創算法來解決這個問題——CRUSH 算法。

五、持久化

對於文件系統來說,持久化是根本,只要 Client 收到了 Server 保存成功的迴應之後,數據就不應該丟失。這主要是通過多副本的方式來解決,但在分佈式環境下,多副本有這幾個問題要面對。

  • 如何保證每個副本的數據是一致的?
  • 如何分散副本,以使災難發生時,不至於所有副本都被損壞?
  • 怎麼檢測被損壞或數據過期的副本,以及如何處理?
  • 該返回哪個副本給 Client?

1、如何保證每個副本的數據是一致的?

同步寫入是保證副本數據一致的最直接的辦法。當 Client 寫入一個文件的時候,Server 會等待所有副本都被成功寫入,再返回給 Client。

這種方式簡單、有保障,唯一的缺陷就是性能會受到影響。假設有 3 個副本,如果每個副本需要N秒,則可能會阻塞 Client 3N 秒的時間,有幾種方式,可以對其進行優化:

  • 並行寫:由一個副本作爲主副本,並行發送數據給其他副本;
  • 鏈式寫:幾個副本組成一個鏈 (chain),並不是等內容都接受到了再往後傳播,而是像流一樣,邊接收上游傳遞過來的數據,一邊傳遞給下游。

還有一種方式是採用 CAP 中所說的 W+R>N 的方式,比如 3 副本 (N=3) 的情況,W=2,R=2,即成功寫入 2 個就認爲成功,讀的時候也要從 2 個副本中讀。這種方式通過犧牲一定的讀成本,來降低寫成本,同時增加寫入的可用性。這種方式在分佈式文件系統中用地比較少。

2、如何分散副本,以使災難發生時,不至於所有副本都被損壞?

這主要避免的是某機房或某城市發生自然環境故障的情況,所以有一個副本應該分配地比較遠。它的副作用是會帶來這個副本的寫入性能可能會有一定的下降,因爲它離 Client 最遠。所以如果在物理條件上無法保證夠用的網絡帶寬的話,則讀寫副本的策略上需要做一定考慮。

可以參考同步寫入只寫 2 副本、較遠副本異步寫入的方式,同時爲了保證一致性,讀取的時候又要注意一些,避免讀取到異步寫入副本的過時數據。

3、怎麼檢測被損壞或數據過期的副本,以及如何處理?

如果有中心節點,則數據節點定期和中心節點進行通信,彙報自己的數據塊的相關信息,中心節點將其與自己維護的信息進行對比。如果某個數據塊的 checksum 不對,則表明該數據塊被損壞了;如果某個數據塊的 version 不對,則表明該數據塊過期了。

如果沒有中心節點,以 ceph 爲例,它在自己的節點集羣中維護了一個比較小的 monitor 集羣,數據節點向這個 monitor 集羣彙報自己的情況,由其來判定是否被損壞或過期。

當發現被損壞或過期副本,將它從 meta 信息中移除,再重新創建一份新的副本就好了,移除的副本在隨後的回收機制中會被收回。

4、該返回哪個副本給 Client?

這裏的策略就比較多了,比如 round-robin、速度最快的節點、成功率最高的節點、CPU 資源最空閒的節點、甚至就固定選第一個作爲主節點,也可以選擇離自己最近的一個,這樣對整體的操作完成時間會有一定節約。

點擊關注公衆號,Java乾貨及時送達

六、伸縮性

1、存儲節點的伸縮

當在集羣中加入一臺新的存儲節點,則它主動向中心節點註冊,提供自己的信息,當後續有創建文件或者給已有文件增加數據塊的時候,中心節點就可以分配到這臺新節點了,比較簡單。但有一些問題需要考慮。

  • 如何儘量使各存儲節點的負載相對均衡?
  • 怎樣保證新加入的節點,不會因短期負載壓力過大而崩塌?
  • 如果需要數據遷移,那如何使其對業務層透明?

1)如何儘量使各存儲節點的負載相對均衡?

首先要有評價存儲節點負載的指標。有多種方式,可以從磁盤空間使用率考慮,也可以從磁盤使用率 +CPU 使用情況 + 網絡流量情況等做綜合判斷。一般來說,磁盤使用率是核心指標。

其次在分配新空間的時候,優先選擇資源使用率小的存儲節點;而對已存在的存儲節點,如果負載已經過載、或者資源使用情況不均衡,則需要做數據遷移。

2)怎樣保證新加入的節點,不會因短期負載壓力過大而崩塌?

當系統發現當前新加入了一臺存儲節點,顯然它的資源使用率是最低的,那麼所有的寫流量都路由到這臺存儲節點來,那就可能造成這臺新節點短期負載過大。因此,在資源分配的時候,需要有預熱時間,在一個時間段內,緩慢地將寫壓力路由過來,直到達成新的均衡。

3)如果需要數據遷移,那如何使其對業務層透明?

在有中心節點的情況下,這個工作比較好做,中心節點就包辦了——判斷哪臺存儲節點壓力較大,判斷把哪些文件遷移到何處,更新自己的 meta 信息,遷移過程中的寫入怎麼辦,發生重命名怎麼辦。無需上層應用來處理。

如果沒有中心節點,那代價比較大,在系統的整體設計上,也是要考慮到這種情況,比如ceph,它要採取邏輯位置和物理位置兩層結構,對Client暴露的是邏輯層 (pool 和 place group),這個在遷移過程中是不變的,而下層物理層數據塊的移動,只是邏輯層所引用的物理塊的地址發生了變化,在Client看來,邏輯塊的位置並不會發生改變。

2、中心節點的伸縮

如果有中心節點,還要考慮它的伸縮性。由於中心節點作爲控制中心,是主從模式,那麼在伸縮性上就受到比較大的限制,是有上限的,不能超過單臺物理機的規模。我們可以考慮各種手段,儘量地擡高這個上限。有幾種方式可以考慮:

七、高可用性

1、中心節點的高可用

中心節點的高可用,不僅要保證自身應用的高可用,還得保證 meta data 的數據高可用。Spring Boot 學習筆記,這個分享給你。

meta data 的高可用主要是數據持久化,並且需要備份機制保證不丟。一般方法是增加一個從節點,主節點的數據實時同步到從節點上。也有采用共享磁盤,通過 raid1 的硬件資源來保障高可用。顯然增加從節點的主備方式更易於部署。

meta data 的數據持久化策略有以下幾種方式:

當前內存服務 + 日誌文件持久化是主流方式。一是純內存操作,效率很高,日誌文件的寫也是順序寫;二是不依賴外部組件,獨立部署。

爲了解決日誌文件會隨着時間增長越來越大的問題,以讓系統能以儘快啓動和恢復,需要輔助以內存快照的方式——定期將內存 dump 保存,只保留在 dump 時刻之後的日誌文件。這樣當恢復時,從最新一次的內存 dump 文件開始,找其對應的 checkpoint 之後的日誌文件開始重播。

2、存儲節點的高可用

在前面“持久化”章節,在保證數據副本不丟失的情況下,也就保證了其的高可用性。另外,分佈式系列面試題和答案全部整理好了,微信搜索Java技術棧,在後臺發送:面試,可以在線閱讀。

八、性能優化和緩存一致性

這些年隨着基礎設施的發展,局域網內千兆甚至萬兆的帶寬已經比較普遍,以萬兆計算,每秒傳輸大約 1250M 字節的數據,而 SATA 磁盤的讀寫速度這些年基本達到瓶頸,在 300-500M/s 附近,也就是純讀寫的話,網絡已經超過了磁盤的能力,不再是瓶頸了,像 NAS 網絡磁盤這些年也開始普及起來。

但這並不代表,沒有必要對讀寫進行優化,畢竟網絡讀寫的速度還是遠慢於內存的讀寫。常見的優化方法主要有:

  • 內存中緩存文件內容;
  • 預加載數據塊,以避免客戶端等待;
  • 合併讀寫請求,也就是將單次請求做些積累,以批量方式發送給 Server 端。

緩存的使用在提高讀寫性能的同時,也會帶來數據不一致的問題:

  • 會出現更新丟失的現象。當多個 Client 在一個時間段內,先後寫入同一個文件時,先寫入的 Client 可能會丟失其寫入內容,因爲可能會被後寫入的 Client 的內容覆蓋掉;
  • 數據可見性問題。Client 讀取的是自己的緩存,在其過期之前,如果別的 Client 更新了文件內容,它是看不到的;也就是說,在同一時間,不同 Client 讀取同一個文件,內容可能不一致。

這類問題有幾種方法:

  • 文件只讀不改:一旦文件被 create 了,就只能讀不能修改。這樣 Client 端的緩存,就不存在不一致的問題;
  • 通過鎖:用鎖的話還要考慮不同的粒度。寫的時候是否允許其他 Client 讀? 讀的時候是否允許其他 Client 寫? 這是在性能和一致性之間的權衡,作爲文件系統來說,由於對業務並沒有約束性,所以要做出合理的權衡,比較困難,因此最好是提供不同粒度的鎖,由業務端來選擇。但這樣的副作用是,業務端的使用成本擡高了。

九、安全性

由於分佈式文件存儲系統,肯定是一個多客戶端使用、多租戶的一個產品,而它又存儲了可能是很重要的信息,所以安全性是它的重要部分。

主流文件系統的權限模型有以下這麼幾種:

市面上的分佈式文件系統有不同的選擇,像 ceph 就提供了類似 DAC 但又略有區別的權限體系,Hadoop 自身就是依賴於操作系統的權限框架,同時其生態圈內有 Apache Sentry 提供了基於 RBAC 的權限體系來做補充。

十、其他

1、空間分配

有連續空間和鏈表空間兩種。連續空間的優勢是讀寫快,按順序即可,劣勢是造成磁盤碎片,更麻煩的是,隨着連續的大塊磁盤空間被分配滿而必須尋找空洞時,連續分配需要提前知道待寫入文件的大小,以便找到合適大小的空間,而待寫入文件的大小,往往又是無法提前知道的 (比如可編輯的 word 文檔,它的內容可以隨時增大);

而鏈表空間的優勢是磁盤碎片很少,劣勢是讀寫很慢,尤其是隨機讀,要從鏈表首個文件塊一個一個地往下找。

爲了解決這個問題,出現了索引表——把文件和數據塊的對應關係也保存一份,存在索引節點中 (一般稱爲 i 節點),操作系統會將 i 節點加載到內存,從而程序隨機尋找數據塊時,在內存中就可以完成了。通過這種方式來解決磁盤鏈表的劣勢,如果索引節點的內容太大,導致內存無法加載,還有可能形成多級索引結構。

2、文件刪除

實時刪除還是延時刪除? 實時刪除的優勢是可以快速釋放磁盤空間;延時刪除只是在刪除動作執行的時候,置個標識位,後續在某個時間點再來批量刪除,它的優勢是文件仍然可以階段性地保留,最大程度地避免了誤刪除,缺點是磁盤空間仍然被佔着。在分佈式文件系統中,磁盤空間都是比較充裕的資源,因此幾乎都採用邏輯刪除,以對數據可以進行恢復,同時在一段時間之後 (可能是 2 天或 3 天,這參數一般都可配置),再對被刪除的資源進行回收。

怎麼回收被刪除或無用的數據? 可以從文件的 meta 信息出發——如果 meta 信息的“文件 - 數據塊”映射表中包含了某個數據塊,則它就是有用的;如果不包含,則表明該數據塊已經是無效的了。所以,刪除文件,其實是刪除 meta 中的“文件 - 數據塊”映射信息 (如果要保留一段時間,則是把這映射信息移到另外一個地方去)。

3、面向小文件的分佈式文件系統

有很多這樣的場景,比如電商——那麼多的商品圖片、個人頭像,比如社交網站——那麼多的照片,它們具有的特性,可以簡單歸納下:

  • 每個文件都不大;
  • 數量特別巨大;
  • 讀多寫少;
  • 不會修改。

針對這種業務場景,主流的實現方式是仍然是以大數據塊的形式存儲,小文件以邏輯存儲的方式存在,即文件 meta 信息記錄其是在哪個大數據塊上,以及在該數據塊上的 offset 和 length 是多少,形成一個邏輯上的獨立文件。這樣既複用了大數據塊系統的優勢和技術積累,又減少了 meta 信息。

4、文件指紋和去重

文件指紋就是根據文件內容,經過算法,計算出文件的唯一標識。如果兩個文件的指紋相同,則文件內容相同。在使用網絡雲盤的時候,發現有時候上傳文件非常地快,就是文件指紋發揮作用。雲盤服務商通過判斷該文件的指紋,發現之前已經有人上傳過了,則不需要真的上傳該文件,只要增加一個引用即可。在文件系統中,通過文件指紋可以用來去重、也可以用來判斷文件內容是否損壞、或者對比文件副本內容是否一致,是一個基礎組件。

文件指紋的算法也比較多,有熟悉的 md5、sha256、也有 google 專門針對文本領域的 simhash 和 minhash 等。

十一、總結

分佈式文件系統內容龐雜,要考慮的問題遠不止上面所說的這些,其具體實現也更爲複雜。本文只是儘量從分佈式文件系統所要考慮的問題出發,給予一個簡要的分析和設計,如果將來遇到類似的場景需要解決,可以想到“有這種解決方案”,然後再來深入研究。

同時,市面上也是存在多種分佈式文件系統的形態,下面就是有研究小組曾經對常見的幾種分佈式文件系統的設計比較。

幾種分佈式文件系統的比較

從這裏也可以看到,選擇其實很多,並不是 GFS 論文中的方式就是最好的。在不同的業務場景中,也可以有更多的選擇策略。最後,關注公衆號Java技術棧,在後臺回覆:面試,可以獲取我整理的 Java 分佈式系列面試題和答案,非常齊全。






關注Java技術棧看更多幹貨



獲取 Spring Boot 實戰筆記!

本文分享自微信公衆號 - Java技術棧(javastack)。
如有侵權,請聯繫 [email protected] 刪除。
本文參與“OSC源創計劃”,歡迎正在閱讀的你也加入,一起分享。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章