本地文件系統如ext3,reiserfs等(這裏不討論基於內存的文件系統),它們管理本地的磁盤存儲資源、提供文件到存儲位置的映射,並抽象出一套文件訪問接口供用戶使用。但隨着互聯網企業的高速發展,這些企業對數據存儲的要求愈來愈高,並且模式各異,如淘寶主站的大量商品圖片,其特色是文件較小,但數量巨大;而相似於youtube,優酷這樣的視頻服務網站,其後臺存儲着大量的視頻文件,尺寸大多在數十兆到數吉字節不等。這些應用場景都是傳統文件系統不能解決的。分佈式文件系統將數據存儲在物理上分散的多個存儲節點上,對這些節點的資源進行統一的管理與分配,並向用戶提供文件系統訪問接口,其主要解決了本地文件系統在文件大小、文件數量、打開文件數等的限制問題。php
目前比較主流的一種分佈式文件系統架構,以下圖所示,一般包括主控服務器(或稱元數據服務器、名字服務器等,一般會配置備用主控服務器以便在故障時接管服務,也能夠兩個都爲主的模式),多個數據服務器(或稱存儲服務器,存儲節點等),以及多個客戶端,客戶端能夠是各類應用服務器,也能夠是終端用戶。java
分佈式文件系統的數據存儲解決方案,歸根結底是將將大問題劃分爲小問題。大量的文件,均勻分佈到多個數據服務器上後,每一個數據服務器存儲的文件數量就少了,另外經過使用大文件存儲多個小文件的方式,總能把單個數據服務器上存儲的文件數降到單機能解決的規模;對於很大的文件,將大文件劃分紅多個相對較小的片斷,存儲在多個數據服務器上(目前,不少本地文件系統對超大文件的支持已經不存在問題了,如ext3文件系統使用4k塊時,文件最大能到4T,ext4則能支持更大的文件,只是受限於磁盤的存儲空間)。python
理論上,分佈式文件系統能夠只有客戶端和多個數據服務器組成,客戶端根據文件名決定將文件存儲到哪一個數據服務器,但一旦有數據服務器失效時,問題就變得複雜,客戶端並不知道數據服務器宕機的消息,仍然鏈接它進行數據存取,致使整個系統的可靠性極大的下降,並且徹底有客戶端決定數據分配時很是不靈活的,其不能根據文件特性制定不一樣的分佈策略。linux
因而,咱們迫切的須要能知道各個數據服務器的服務狀態,數據服務器的狀態管理可分爲分散式和集中式兩種方式,前者是讓多個數據服務器相互管理,如每一個服務器向其餘全部的服務器發送心跳信息,但這種方式開銷較大,控制很差容易影響到正常的數據服務,並且工程實現較爲複雜;後者是指經過一個獨立的服務器(如上圖中的主控服務器)來管理數據服務器,每一個服務器向其彙報服務狀態來達到集中管理的目的,這種方式簡單易實現,目前不少分佈式文件系統都採用這種方式如GFS、TFS(http://code.taobao.org/p/tfs/wiki/index/ )、MooseFS (http://www.moosefs.org/ )等。主控服務器在負載較大時會出現單點,較多的解決方案是配置備用服務器,以便在故障時接管服務,若是須要,主備之間須要進行數據的同步。算法
本文主要討論基於上圖架構的分佈式文件系統的相關原理,工程實現時須要解決的問題和解決問題的基本方法,分佈式文件系統涉及的主要問題及解決方法以下圖所示。爲方便描述如下主控服務器簡稱Master,數據服務器簡稱DS(DataServer)。數據庫
Master負責維護整個文件系統的命名空間,並暴露給用戶使用,命名空間的結構主要有典型目錄樹結構如MooseFS等,扁平化結構如淘寶TFS(目前已提供目錄樹結構支持),圖結構(主要面向終端用戶,方便用戶根據文件關聯性組織文件,只在論文中看到過)。緩存
爲了維護名字空間,須要存儲一些輔助的元數據如文件(塊)到數據服務器的映射關係,文件之間的關係等,爲了提高效率,不少文件系統採起將元數據所有內存化(元數據一般較小)的方式如GFS, TFS;有些系統借則助數據庫來存儲元數據如DBFS,還有些系統則採用本地文件來存儲元數據如MooseFS。安全
一種簡單的實現目錄樹結構的方式是,在Master上存儲與客戶端徹底同樣的命名空間,對應的文件內容爲該文件的元數據,並經過在Master上採用ReiserFS來進行小文件存儲優化,對於大文件的存儲(文件數量不會成爲Master的瓶頸),這種方式簡單易實現。曾經參與的DNFS系統的開發就是使用這種方式,DNFS主要用於存儲視頻文件,視頻數量在百萬級別,Master採用這種方式文件數量上不會成爲瓶頸。服務器
除了維護文件系統的命名空間,Master還須要集中管理數據DS, 可經過輪詢DS或由DS報告心跳的方式實現。在接收到客戶端寫請求時,Master須要根據各個DS的負載等信息選擇一組(根據系統配置的副本數)DS爲其服務;當Master發現有DS宕機時,須要對一些副本數不足的文件(塊)執行復制計劃;當有新的DS加入集羣或是某個DS上負載太高,Master也可根據須要執行一些副本遷移計劃。網絡
若是Master的元數據存儲是非持久化的,則在DS啓動時還須要把本身的文件(塊)信息彙報給Master。在分配DS時,基本的分配方法有隨機選取,RR輪轉、低負載優先等,還能夠將服務器的部署做爲參考(如HDFS分配的策略),也能夠根據客戶端的信息,將分配的DS按照與客戶端的遠近排序,使得客戶端優先選取離本身近的DS進行數據存取.
Master最終的目的仍是要服務好客戶端的請求,除了一些週期性線程任務外,Master須要服務來自客戶端和DS的請求,一般的服務模型包括單線程、每請求一線程、線程池(一般配合任務隊列)。單線程模型下,Master只能順序的服務請求,該方式效率低,不能充分利用好系統資源;每請求一線程的方式雖能併發的處理請求,但因爲系統資源的限制,致使建立線程數存在限制,從而限制同時服務的請求數量,另外,線程太多,線程間的調度效率也是個大問題;線程池的方式目前使用較多,一般由單獨的線程接受請求,並將其加入到任務隊列中,而線程池中的線程則從任務隊列中不斷的取出任務進行處理。
Master在整個分佈式文件系統中的做用很是重要,其維護文件(塊)到DS的映射、管理全部的DS狀態並在某些條件觸發時執行負載均衡計劃等。爲了不Master的單點問題,一般會爲其配置備用服務器,以保證在主控服務器節點失效時接管其工做。一般的實現方式是經過HA、UCARP等軟件爲主備服務器提供一個虛擬IP提供服務,當備用服務器檢測到主宕機時,會接管主的資源及服務。
若是Master須要持久化一些數據,則須要將數據同步到備用Master,對於元數據內存化的狀況,爲了加速元數據的構建,有時也需將主上的操做同步到備Master。處理方式可分爲同步和異步兩種。同步方式將每次請求同步轉發至備Master,這樣理論上主備時刻保持一致的狀態,但這種方式會增長客戶端的響應延遲(在客戶端對響應延遲要求不高時可以使用這種方式),當備Master宕機時,可採起不作任何處理,等備Master起來後再同步數據,或是暫時中止寫服務,管理員介入啓動備Master再正常服務(需業務能容忍);異步方式則是先暫存客戶端的請求信息(如追加至操做日誌),後臺線程重放日誌到備Master,這種方式會使得主備的數據存在不一致的狀況,具體策略需針對需求制定。
數據服務器負責文件數據在本地的持久化存儲,最簡單的方式是將客戶每一個文件數據分配到一個單獨的DS上做爲一個本地文件存儲,但這種方式並不能很好的利用分佈式文件系統的特性,不少文件系統使用固定大小的塊來存儲數據如GFS, TFS, HDFS,典型的塊大小爲64M。
對於小文件的存儲,能夠將多個文件的數據存儲在一個塊中,併爲塊內的文件創建索引,這樣能夠極大的提升存儲空間利用率。Facebook用於存儲照片的HayStack系統的本地存儲方式爲,將多個圖片對象存儲在一個大文件中,併爲每一個文件的存儲位置創建索引,其支持文件的建立和刪除,不支持更新(經過刪除和建立完成),新建立的圖片追加到大文件的末尾並更新索引,文件刪除時,簡單的設置文件頭的刪除標記,系統在空閒時會對大文件進行compact把設置刪除標記且超過必定時限的文件存儲空間回收(延遲刪除策略)。淘寶的TFS系統採用了相似的方式,對小文件的存儲進行了優化,TFS使用擴展塊的方式支持文件的更新。對小文件的存儲也可直接藉助一些開源的KV存儲解決方案,如Tokyo Cabinet(HDB, FDB, BDB, TDB)、Redis等。
對於大文件的存儲,則可將文件存儲到多個塊上,多個塊所在的DS能夠並行服務,這種需求一般不須要對本地存儲作太多優化。
DS除了簡單的存儲數據外,還須要維護一些狀態,首先它須要將本身的狀態以心跳包的方式週期性的報告給Master,使得Master知道本身是否正常工做,一般心跳包中還會包含DS當前的負載情況(CPU、內存、磁盤IO、磁盤存儲空間、網絡IO等、進程資源,視具體需求而定),這些信息能夠幫助Master更好的制定負載均衡策略。
不少分佈式文件系統如HDFS在外圍提供一套監控系統,能夠實時的獲取DS或Master的負載情況,管理員可根據監控信息進行故障預防。
爲了保證數據的安全性,分佈式文件系統中的文件會存儲多個副本到DS上,寫多個副本的方式,主要分爲3種。最簡單的方式是客戶端分別向多個DS寫同一份數據,如DNFS採用這種方式;第2種方式是客戶端向主DS寫數據,主DS向其餘DS轉發數據,如TFS採用這種方式;第三種方式採用流水複製的方式,client向某個DS寫數據,該DS向副本鏈中下一個DS轉發數據,依次類推,如HDFS、GFS採起這種方式。
當有節點宕機或節點間負載極不均勻的狀況下,Master會制定一些副本複製或遷移計劃,而DS實際執行這些計劃,將副本轉發或遷移至其餘的DS。DS也可提供管理工具,在須要的狀況下由管理員手動的執行一些複製或遷移計劃。
參考主控服務器::服務模型一節
用戶最終經過文件系統提供的接口來存取數據,linux環境下,最好莫過於能提供POSIX接口的支持,這樣不少應用(各類語言皆可,最終都是系統調用)能不加修改的將本地文件存儲替換爲分佈式文件存儲。
要想文件系統支持POSIX接口,一種方式時按照VFS接口規範實現文件系統,這種方式須要文件系統開發者對內核有必定的瞭解;另外一種方式是藉助FUSE(http://fuse.sourceforge.net)軟件,在用戶態實現文件系統並能支持POSIX接口,可是用該軟件包開發的文件系統會有額外的用戶態內核態的切換、數據拷貝過程,從而致使其效率不高。不少文件系統的開發藉助了fuse,參考http://sourceforge.net/apps/mediawiki/fuse/index.php?title=FileSystems。
若是不能支持POSIX接口,則爲了支持不一樣語言的開發者,須要提供多種語言的客戶端支持,如經常使用的C/C++、java、php、python客戶端。使用客戶端的方式較難處理的一種狀況時,當客戶端升級時,使用客戶端接口的應用要使用新的功能,也須要進行升級,當應用較多時,升級過程很是麻煩。目前一種趨勢是提供Restful接口的支持,使用http協議的方式給應用(用戶)訪問文件資源,這樣就避免功能升級帶來的問題。
另外,在客戶端接口的支持上,也需根據系統需求權衡,好比write接口,在分佈式實現上較麻煩,很難解決數據一致性的問題,應該考慮可否只支持create(update經過delete和create組合實現),或折中支持append,以下降系統的複雜性。
分佈式文件系統的文件存取,要求客戶端先鏈接Master獲取一些用於文件訪問的元信息,這一過程一方面加劇了Master的負擔,一方面增長了客戶端的請求的響應延遲。爲了加速該過程,同時減少Master的負擔,可將元信息進行緩存,數據可根據業務特性緩存在本地內存或磁盤,也可緩存在遠端的cache系統上如淘寶的TFS可利用tair做爲緩存(減少Master負擔、下降客戶端資源佔用)。
維護緩存需考慮如何解決一致性問題及緩存替換算法,一致性的維護可由客戶端也可由服務器完成,一種方式是客戶端週期性的使cache失效或檢查cache有效性(需業務上能容忍),或由服務器在元數據更新後通知客戶端使cache失效(需維護客戶端狀態)。使用得較多的替換算法如LRU、隨機替換等。
客戶端還能夠根據須要支持一些擴展特性,如將數據進行加密保證數據的安全性、將數據進行壓縮後存儲下降存儲空間使用,或是在接口中封裝一些訪問統計行爲,以支持系統對應用的行爲進行監控和統計。
本文主要從典型分佈式文件系統架構出發,討論了分佈式文件系統的基本原理,工程實現時須要解決的問題、以及解決問題的基本方法,真正在系統工程實現時,要考慮的問題會更多。若有問題,歡迎拍磚。