我們知道 HDFS 最早是根據 GFS(Google File System)的論文概念模型來設計實現的。
然后呢,我就去把 GFS 的原始論文找出來仔細看了遍,GFS 的整體架構圖如下:
HDFS 參照了它所以大部分架構設計概念是類似的,比如 HDFS NameNode 相當於 GFS Master,HDFS DataNode 相當於 GFS chunkserver。
但還有些細節不同的地方,所以本文主要分析下不同的地方。
寫入模型
HDFS 在考慮寫入模型時做了一個簡化,就是同一時刻只允許一個寫入者或追加者。
在這個模型下同一個文件同一個時刻只允許一個客戶端寫入或追加。
而 GFS 則允許同一時刻多個客戶端並發寫入或追加同一文件。
允許並發寫入帶來了更復雜的一致性問題。
多個客戶端並發寫入時,它們之間的順序是無法保證的,同一個客戶端連續追加成功的多個記錄也可能被打斷。
這意味着一個客戶端在連續寫入文件數據時,它的數據最終在文件中的分布可能是不連續的。
所謂一致性就是,對同一個文件,所有的客戶端看到的數據是一致的,不管它們是從哪個副本讀取的。
如果允許多個客戶端同時寫一個文件,怎么保證寫入數據在多個副本間一致?
我們前面講 HDFS 時它只允許一個寫入者按流水線方式寫入多個副本,寫入順序一致,寫入完成后數據將保持最終一致。
而對多個客戶端而言,就必須讓所有同時寫入的客戶端按同一種流水線方式去寫入,才可能保證寫入順序一致。
這個寫入流程我們下一節詳細分析。
寫入流程
GFS 使用租約機制來保障在跨多個副本的數據寫入中保持順序一致性。
GFS Master 將 chunk 租約發放給其中一個副本,這個副本我們就稱為主副本,其他副本稱為次副本。
由主副本來確定一個針對該 chunk 的寫入順序,次副本則遵守這個順序,這樣就保障了全局順序一致性。
chunk 租約機制的設計主要是為了減輕 Master 的負擔,由主副本所在的 chunkserver 來承擔流水線順序的安排。
如下圖,我們詳細描述下這個過程。
- 客戶端請求 Master 詢問哪個 chunkserver 持有租約以及其他副本的位置。
如果沒有 chunkserver 持有租約,說明該 chunk 最近沒有寫操作。
Master 則選擇將租約授權給其中一台 chunkserver。 - Master 返回客戶端主副本和次副本的位置信息。
客戶端緩存這些信息以備將來使用。
客戶端以后不再需要聯系 Master,除非主副本所在 chunkserver 不可用或返回租約過期了。 - 客戶端選擇最優的網絡順序推送數據,chunkserver 將數據先緩存在內部的 LRU 緩存中。
GFS 中采用數據流和控制流分離的方法,從而能夠基於網絡拓撲結構更好地調度數據流的傳輸。 - 一旦所有的副本確認收到了數據,客戶端將發送一個寫請求控制命令到主副本。
由主副本分配連續的序列號來確定最終的寫入順序。 - 主副本轉發寫請求到所有次副本,次副本按主副本安排的順序執行寫入操作。
- 次副本寫完后向主副本應答確認操作完成。
- 最后主副本應答客戶端,若任意副本寫入過程中出現錯誤,將報告給客戶端,由客戶端發起重試。
GFS 和 HDFS 的寫入流程都采用了流水線方式,但 HDFS 沒有分離數據流和控制流。
HDFS 的數據流水線寫入在網絡上的傳輸順序與最終寫入文件的順序一致。
而 GFS 數據在網絡上的傳輸順序與最終寫入文件的順序可能不一致。
GFS 在支持並發寫入和優化網絡數據傳輸方面做出了最佳的折衷。
首先,有一點要確認的是,作為GFS的一個最重要的實現,HDFS設計目標和GFS是高度一致的。在架構、塊大小、元數據等的實現上,HDFS與GFS大致一致。但是,在某些地方,HDFS與GFS又有些不同。如: 1、 快照(Snapshot): GFS中的快照功能是非常強大的,可以非常快的對文件或者目錄進行拷貝,並且不影響當前操作(讀/寫/復制)。GFS中生成快照的方式叫copy-on-write。也就是說,文件的備份在某些時候只是將快照文件指向原chunk,增加對chunk的引用計數而已,等到chunk上進行了寫操作時,Chunk Server才會拷貝chunk塊,后續的修改操作落到新生成的chunk上。 而HDFS暫時並不支持快照功能,而是運用最基礎的復制來完成。想象一下,當HBase上的數據在進行重新划分時(過程類似於hash平衡),HDFS需要對其中的所有數據(P/T級的)進行復制遷移,而GFS只需要快照,多不方便! 2、 記錄追加操作(append): 在數據一致性方面,GFS在理論上相對HDFS更加完善。 a) GFS提供了一個相對寬松的一致性模型。GFS同時支持寫和記錄追加操作。寫操作使得我們可以隨機寫文件。記錄追加操作使得並行操作更加安全可靠。 b) HDFS對於寫操作的數據流和GFS的功能一樣。但是,HDFS並不支持記錄追加和並行寫操作。NameNode用INodeFileUnderConstruction屬性標記正在進行操作的文件塊,而不關注是讀還是寫。DataNode甚至看不到租約!一個文件一旦創建、寫入、關閉之后就不需要修改了。這樣的簡單模型適合於Map/Reduce編程。 3、 垃圾回收(GC): a) GFS垃圾回收采用惰性回收策略,即master並不會立即回收程序所刪除的文件資源。 GFS選擇以一種特定的形式標記刪除文件(通常是將文件名改為一個包含時間信息的隱藏名字),這樣的文件不再被普通用戶所訪問。Master會定期對文件的命名空間進行檢查,並刪除一段時間前的隱藏文件(默認3天)。 b) HDFS並沒有采用這樣的垃圾回收機制,而是采取了一種更加簡單但是更容易實現的直接刪除方式。 c) 應該說延遲回收和直接刪除各有優勢。延遲回收為那些“不小心“的刪除操作留了后路。同時,回收資源的具體操作時在Master結點空閑時候完成,對GFS的性能有很好的提高。但是延遲回收會占用很大的存儲空間,假如某些可惡的用戶無聊了一直創建刪除文件怎么辦? 試分析下這種不同。有人說,GFS在功能上非常完善,非常強大,而HDFS在策略上較之簡單些,主要是為了有利於實現。但實際上,GFS作為存儲平台早已經被廣泛的部署在Google內部,存儲Google服務產生或者要處理的數據,同時用於大規模數據集的研究與開發工作。因此GFS並不僅僅是理論上的研究,而是具體實現。作為GFS的后輩與開源實現,HDFS在技術上應該是更加成熟的,不可能為了“偷懶”而簡化功能。因此,簡化說應該是不成立的。 個人認為,GFS與HDFS的不同是由於“專”與“通”的區別。眾所周知,Hadoop是一個開源軟件/框架,在設計之初就考慮到了用戶(面向世界上的所有個人、企業)在需求上的差異,比如數據密集型(如淘寶的數據存儲)、計算密集型(百度的PR算法)、混合型等等。而GFS在設計之初就對目標比較明確,都是Google的嘛,因此GFS可以對其主要功能進行性能上的優化。