【轉】谷歌三大核心技術(一)The Google File System中文版

 

The Google File System中文版

譯者:alex

摘要

咱們設計並實現了Google GFS文件系統,一個面向大規模數據密集型應用的、可伸縮的分佈式文件系統。GFS雖然運行在廉價的廣泛硬件設備上,可是它依然了提供災難冗餘的能力,爲大量客戶機提供了高性能的服務。
 
雖 然GFS的設計目標與許多傳統的分佈式文件系統有不少相同之處,可是,咱們的設計仍是以咱們對本身的應用的負載狀況和技術環境的分析爲基礎的,無論如今還 是未來,GFS和早期的分佈式文件系統的設想都有明顯的不一樣。因此咱們從新審視了傳統文件系統在設計上的折衷選擇,衍生出了徹底不一樣的設計思路。
 
GFS 徹底知足了咱們對存儲的需求。GFS做爲存儲平臺已經被普遍的部署在Google內部,存儲咱們的服務產生和處理的數據,同時還用於那些須要大規模數據集 的研究和開發工做。目前爲止,最大的一個集羣利用數千臺機器的數千個硬盤,提供了數百TB的存儲空間,同時爲數百個客戶機服務。
 
在本論文中,咱們展現了可以支持分佈式應用的文件系統接口的擴展,討論咱們設計的許多方面,最後列出了小規模性能測試以及真實生產系統中性能相關數據。
 

分類和主題描述

D [4]: 3—D分佈文件系統

經常使用術語

設計,可靠性,性能,測量

關鍵詞

容錯,可伸縮性,數據存儲,集羣存儲

1. 簡介

爲 了知足Google迅速增加的數據處理需求,咱們設計並實現了Google文件系統(Google File System – GFS)。GFS與傳統的分佈式文件系統有着不少相同的設計目標,好比,性能、可伸縮性、可靠性以及可用性。可是,咱們的設計還基於咱們對咱們本身的應用 的負載狀況和技術環境的觀察的影響,無論如今仍是未來,GFS和早期文件系統的假設都有明顯的不一樣。因此咱們從新審視了傳統文件系統在設計上的折衷選擇, 衍生出了徹底不一樣的設計思路。
 
首先,組件失效被認爲是常態事件,而不是 意外事件。GFS包括幾百甚至幾千臺普通的廉價設備組裝的存儲機器,同時被至關數量的客戶機訪問。GFS組件的數量和質量致使在事實上,任何給定時間內都 有可能發生某些組件沒法工做,某些組件沒法從它們目前的失效狀態中恢復。咱們遇到過各類各樣的問題,好比應用程序bug、操做系統的bug、人爲失誤,甚 至還有硬盤、內存、鏈接器、網絡以及電源失效等形成的問題。因此,持續的監控、錯誤偵測、災難冗餘以及自動恢復的機制必須集成在GFS中。
 
其 次,以一般的標準衡量,咱們的文件很是巨大。數GB的文件很是廣泛。每一個文件一般都包含許多應用程序對象,好比web文檔。當咱們常常須要處理快速增加 的、而且由數億個對象構成的、數以TB的數據集時,採用管理數億個KB大小的小文件的方式是很是不明智的,儘管有些文件系統支持這樣的管理方式。所以,設 計的假設條件和參數,好比I/O操做和Block的尺寸都須要從新考慮。
 
第 三,絕大部分文件的修改是採用在文件尾部追加數據,而不是覆蓋原有數據的方式。對文件的隨機寫入操做在實際中幾乎不存在。一旦寫完以後,對文件的操做就只 有讀,並且一般是按順序讀。大量的數據符合這些特性,好比:數據分析程序掃描的超大的數據集;正在運行的應用程序生成的連續的數據流;存檔的數據;由一臺 機器生成、另一臺機器處理的中間數據,這些中間數據的處理多是同時進行的、也多是後續才處理的。對於這種針對海量文件的訪問模式,客戶端對數據塊緩 存是沒有意義的,數據的追加操做是性能優化和原子性保證的主要考量因素。
 
第 四,應用程序和文件系統API的協同設計提升了整個系統的靈活性。好比,咱們放鬆了對GFS一致性模型的要求,這樣就減輕了文件系統對應用程序的苛刻要 求,大大簡化了GFS的設計。咱們引入了原子性的記錄追加操做,從而保證多個客戶端可以同時進行追加操做,不須要額外的同步操做來保證數據的一致性。本文 後面還有對這些問題的細節的詳細討論。
 
Google已經針對不一樣的應用部署了多套GFS集羣。最大的一個集羣擁有超過1000個存儲節點,超過300TB的硬盤空間,被不一樣機器上的數百個客戶端接二連三的頻繁訪問。 

2.設計概述

2.1設計預期

在設計知足咱們需求的文件系統時候,咱們的設計目標既有機會、又有挑戰。以前咱們已經提到了一些須要關注的關鍵點,這裏咱們將設計的預期目標的細節展開討論。node

  • 系統由許多廉價的普通組件組成,組件失效是一種常態。系統必須持續監控自身的狀態,它必須將組件失效做爲一種常態,可以迅速地偵測、冗餘並恢復失效的組件。
  • 系統存儲必定數量的大文件。咱們預期會有幾百萬文件,文件的大小一般在100MB或者以上。數個GB大小的文件也是廣泛存在,而且要可以被有效的管理。系統也必須支持小文件,可是不須要針對小文件作專門的優化。
  • 系 統的工做負載主要由兩種讀操做組成:大規模的流式讀取和小規模的隨機讀取。大規模的流式讀取一般一次讀取數百KB的數據,更常見的是一次讀取1MB甚至更 多的數據。來自同一個客戶機的連續操做一般是讀取同一個文件中連續的一個區域。小規模的隨機讀取一般是在文件某個隨機的位置讀取幾個KB數據。若是應用程 序對性能很是關注,一般的作法是把小規模的隨機讀取操做合併並排序,以後按順序批量讀取,這樣就避免了在文件中先後來回的移動讀取位置。
  • 系統的工做負載還包括許多大規模的、順序的、數據追加方式的寫操做。通常狀況下,每次寫入的數據的大小和大規模讀相似。數據一旦被寫入後,文件就不多會被修改了。系統支持小規模的隨機位置寫入操做,可是可能效率不彰。
  • 系統必須高效的、行爲定義明確的 (alex注:well-defined)實現多 客戶端並行追加數據到同一個文件裏的語意。咱們的文件一般被用於」生產者-消費者「隊列,或者其它多路文件合併操做。一般會有數百個生產者,每一個生產者進 程運行在一臺機器上,同時對一個文件進行追加操做。使用最小的同步開銷來實現的原子的多路追加數據操做是必不可少的。文件能夠在稍後讀取,或者是消費者在 追加的操做的同時讀取文件。
  • 高性能的穩定網絡帶寬遠比低延遲重要。咱們的目標程序絕大部分要求可以高速率的、大批量的處理數據,極少有程序對單一的讀寫操做有嚴格的響應時間要求。

2.2 接口

GFS提供了一套相似傳統文件系統的API接口函數,雖然並非嚴格按照POSIX等標準API的形式實現的。文件以分層目錄的形式組織,用路徑名來標識。咱們支持經常使用的操做,如建立新文件、刪除文件、打開文件、關閉文件、讀和寫文件。
 
另 外,GFS提供了快照和記錄追加操做。快照以很低的成本建立一個文件或者目錄樹的拷貝。記錄追加操做容許多個客戶端同時對一個文件進行數據追加操做,同時 保證每一個客戶端的追加操做都是原子性的。這對於實現多路結果合併,以及」生產者-消費者」隊列很是有用,多個客戶端能夠在不須要額外的同步鎖定的狀況下, 同時對一個文件追加數據。咱們發現這些類型的文件對於構建大型分佈應用是很是重要的。快照和記錄追加操做將在3.4和3.3節分別討論。

2.3 架構

一個GFS集羣包含一個單獨的Master節點 (alex 注:這裏的一個單獨的Master節點的含義是GFS系統中只存在一個邏輯上的Master組件。後面咱們還會提到Master節點複製,所以,爲了理解 方便,咱們把Master節點視爲一個邏輯上的概念,一個邏輯的Master節點包括兩臺物理主機,即兩臺Master服務器)、多 臺Chunk服務器,而且同時被多個客戶端訪問,如圖1所示。全部的這些機器一般都是普通的Linux機器,運行着用戶級別(user-level)的服 務進程。咱們能夠很容易的把Chunk服務器和客戶端都放在同一臺機器上,前提是機器資源容許,而且咱們可以接受不可靠的應用程序代碼帶來的穩定性下降的 風險。
GFS 存儲的文件都被分割成固定大小的Chunk。在Chunk建立的時候,Master服務器會給每一個Chunk分配一個不變的、全球惟一的64位的 Chunk標識。Chunk服務器把Chunk以linux文件的形式保存在本地硬盤上,而且根據指定的Chunk標識和字節範圍來讀寫塊數據。出於可靠 性的考慮,每一個塊都會複製到多個塊服務器上。缺省狀況下,咱們使用3個存儲複製節點,不過用戶能夠爲不一樣的文件命名空間設定不一樣的複製級別。
 
Master節點管理全部的文件系統元數據。這些元數據包括名字空間、訪問控制信息、文件和Chunk的映射信息、以及當前Chunk的位置信息。Master節點還管理着系統範圍內的活動,好比,Chunk租用管理 (alex注:BDB也有關於lease的描述,不知道是否相同)、孤兒Chunk (alex注:orphaned chunks)的回收、以及Chunk在Chunk服務器之間的遷移。Master節點使用心跳信息週期地和每一個Chunk服務器通信,發送指令到各個Chunk服務器並接收Chunk服務器的狀態信息。
 
GFS 客戶端代碼以庫的形式被連接到客戶程序裏。客戶端代碼實現了GFS文件系統的API接口函數、應用程序與Master節點和Chunk服務器通信、以及對 數據進行讀寫操做。客戶端和Master節點的通訊只獲取元數據,全部的數據操做都是由客戶端直接和Chunk服務器進行交互的。咱們不提供POSIX標 準的API的功能,所以,GFS API調用不須要深刻到Linux vnode級別。
 
無 論是客戶端仍是Chunk服務器都不須要緩存文件數據。客戶端緩存數據幾乎沒有什麼用處,由於大部分程序要麼以流的方式讀取一個巨大文件,要麼工做集太大 根本沒法被緩存。無需考慮緩存相關的問題也簡化了客戶端和整個系統的設計和實現。(不過,客戶端會緩存元數據。)Chunk服務器不須要緩存文件數據的原 因是,Chunk以本地文件的方式保存,Linux操做系統的文件系統緩存會把常常訪問的數據緩存在內存中。

2.4 單一Master節點

單 一的Master節點的策略大大簡化了咱們的設計。單一的Master節點能夠經過全局的信息精肯定位Chunk的位置以及進行復制決策。另外,咱們必須 減小對Master節點的讀寫,避免Master節點成爲系統的瓶頸。客戶端並不經過Master節點讀寫文件數據。反之,客戶端向Master節點詢問 它應該聯繫的Chunk服務器。客戶端將這些元數據信息緩存一段時間,後續的操做將直接和Chunk服務器進行數據讀寫操做。
 
我 們利用圖1解釋一下一次簡單讀取的流程。首先,客戶端把文件名和程序指定的字節偏移,根據固定的Chunk大小,轉換成文件的Chunk索引。而後,它把 文件名和Chunk索引起送給Master節點。Master節點將相應的Chunk標識和副本的位置信息發還給客戶端。客戶端用文件名和Chunk索引 做爲key緩存這些信息。
 
以後客戶端發送請求到其中的一個副本處,通常 會選擇最近的。請求信息包含了Chunk的標識和字節範圍。在對這個Chunk的後續讀取操做中,客戶端沒必要再和Master節點通信了,除非緩存的元數 據信息過時或者文件被從新打開。實際上,客戶端一般會在一次請求中查詢多個Chunk信息,Master節點的迴應也可能包含了緊跟着這些被請求的 Chunk後面的Chunk的信息。在實際應用中,這些額外的信息在沒有任何代價的狀況下,避免了客戶端和Master節點將來可能會發生的幾回通信。

2.5 Chunk尺寸

Chunk 的大小是關鍵的設計參數之一。咱們選擇了64MB,這個尺寸遠遠大於通常文件系統的Block size。每一個Chunk的副本都以普通Linux文件的形式保存在Chunk服務器上,只有在須要的時候才擴大。惰性空間分配策略避免了因內部碎片形成 的空間浪費,內部碎片或許是對選擇這麼大的Chunk尺寸最具爭議一點。
 
選 擇較大的Chunk尺寸有幾個重要的優勢。首先,它減小了客戶端和Master節點通信的需求,由於只須要一次和Mater節點的通訊就能夠獲取 Chunk的位置信息,以後就能夠對同一個Chunk進行屢次的讀寫操做。這種方式對下降咱們的工做負載來講效果顯著,由於咱們的應用程序一般是連續讀寫 大文件。即便是小規模的隨機讀取,採用較大的Chunk尺寸也帶來明顯的好處,客戶端能夠輕鬆的緩存一個數TB的工做數據集全部的Chunk位置信息。其 次,採用較大的Chunk尺寸,客戶端可以對一個塊進行屢次操做,這樣就能夠經過與Chunk服務器保持較長時間的TCP鏈接來減小網絡負載。第三,選用 較大的Chunk尺寸減小了Master節點須要保存的元數據的數量。這就容許咱們把元數據所有放在內存中,在2.6.1節咱們會討論元數據所有放在內存 中帶來的額外的好處。
 
另外一方面,即便配合惰性空間分配,採用較大的 Chunk尺寸也有其缺陷。小文件包含較少的Chunk,甚至只有一個Chunk。當有許多的客戶端對同一個小文件進行屢次的訪問時,存儲這些Chunk 的Chunk服務器就會變成熱點。在實際應用中,因爲咱們的程序一般是連續的讀取包含多個Chunk的大文件,熱點還不是主要的問題。
 
然 而,當咱們第一次把GFS用於批處理隊列系統的時候,熱點的問題仍是產生了:一個可執行文件在GFS上保存爲single-chunk文件,以後這個可執 行文件在數百臺機器上同時啓動。存放這個可執行文件的幾個Chunk服務器被數百個客戶端的併發請求訪問致使系統局部過載。咱們經過使用更大的複製參數來 保存可執行文件,以及錯開批處理隊列系統程序的啓動時間的方法解決了這個問題。一個可能的長效解決方案是,在這種的狀況下,容許客戶端從其它客戶端讀取數 據。

2.6 元數據

Master服務器 (alex注:注意邏輯的Master節點和物理的Master服務器的區別。後續咱們談的是每一個Master服務器的行爲,如存儲、內存等等,所以咱們將所有使用物理名稱)存 儲3種主要類型的元數據,包括:文件和Chunk的命名空間、文件和Chunk的對應關係、每一個Chunk副本的存放地點。全部的元數據都保存在 Master服務器的內存中。前兩種類型的元數據(命名空間、文件和Chunk的對應關係)同時也會以記錄變動日誌的方式記錄在操做系統的系統日誌文件 中,日誌文件存儲在本地磁盤上,同時日誌會被複制到其它的遠程Master服務器上。採用保存變動日誌的方式,咱們可以簡單可靠的更新Master服務器 的狀態,而且不用擔憂Master服務器崩潰致使數據不一致的風險。Master服務器不會持久保存Chunk位置信息。Master服務器在啓動時,或 者有新的Chunk服務器加入時,向各個Chunk服務器輪詢它們所存儲的Chunk的信息。

2.6.1 內存中的數據結構

因 爲元數據保存在內存中,因此Master服務器的操做速度很是快。而且,Master服務器能夠在後臺簡單而高效的週期性掃描本身保存的所有狀態信息。這 種週期性的狀態掃描也用於實現Chunk垃圾收集、在Chunk服務器失效的時從新複製數據、經過Chunk的遷移實現跨Chunk服務器的負載均衡以及 磁盤使用情況統計等功能。4.3和4.4章節將深刻討論這些行爲。
 
將元 數據所有保存在內存中的方法有潛在問題:Chunk的數量以及整個系統的承載能力都受限於Master服務器所擁有的內存大小。可是在實際應用中,這並不 是一個嚴重的問題。Master服務器只須要不到64個字節的元數據就可以管理一個64MB的Chunk。因爲大多數文件都包含多個Chunk,所以絕大 多數Chunk都是滿的,除了文件的最後一個Chunk是部分填充的。一樣的,每一個文件的在命名空間中的數據大小一般在64字節如下,由於保存的文件名是 用前綴壓縮算法壓縮過的。
 
即使是須要支持更大的文件系統,爲Master服務器增長額外內存的費用是不多的,而經過增長有限的費用,咱們就可以把元數據所有保存在內存裏,加強了系統的簡潔性、可靠性、高性能和靈活性。

2.6.2 Chunk位置信息

Master 服務器並不保存持久化保存哪一個Chunk服務器存有指定Chunk的副本的信息。Master服務器只是在啓動的時候輪詢Chunk服務器以獲取這些信 息。Master服務器可以保證它持有的信息始終是最新的,由於它控制了全部的Chunk位置的分配,並且經過週期性的心跳信息監控Chunk服務器的狀 態。linux

最初設計時,咱們試圖把Chunk的位置信息持久的保存在Master服務器上,可是後來咱們發如今啓動的時候輪詢Chunk服務器, 以後按期輪詢更新的方式更簡單。這種設計簡化了在有Chunk服務器加入集羣、離開集羣、改名、失效、以及重啓的時候,Master服務器和Chunk服 務器數據同步的問題。在一個擁有數百臺服務器的集羣中,這類事件會頻繁的發生。web

能夠從另一個角度去理解這個設計決策:只有Chunk服務 器才能最終肯定一個Chunk是否在它的硬盤上。咱們從沒有考慮過在Master服務器上維護一個這些信息的全局視圖,由於Chunk服務器的錯誤可能會 致使Chunk自動消失(好比,硬盤損壞了或者沒法訪問了),亦或者操做人員可能會重命名一個Chunk服務器。算法

2.6.3 操做日誌

操做日誌包含了關鍵的元數據變動歷史記錄。這對GFS很是重要。這不只僅是由於操做日誌是元數據惟一的持久化存儲記錄,它也做爲判斷同步操做順序的邏輯時間基線(alex注:也就是經過邏輯日誌的序號做爲操做發生的邏輯時間,相似於事務系統中的LSN)。文件和Chunk,連同它們的版本(參考4.5節),都由它們建立的邏輯時間惟一的、永久的標識。數據庫

操 做日誌很是重要,咱們必須確保日誌文件的完整,確保只有在元數據的變化被持久化後,日誌纔對客戶端是可見的。不然,即便Chunk自己沒有出現任何問題, 咱們仍有可能丟失整個文件系統,或者丟失客戶端最近的操做。因此,咱們會把日誌複製到多臺遠程機器,而且只有把相應的日誌記錄寫入到本地以及遠程機器的硬 盤後,纔會響應客戶端的操做請求。Master服務器會收集多個日誌記錄後批量處理,以減小寫入磁盤和複製對系統總體性能的影響。編程

Master服務器在災難恢復時,經過重演操做日誌把文件系統恢復到最近的狀態。爲了縮短Master啓動的時間,咱們必須使日誌足夠小 (alex注:即重演系統操做的日誌量儘可能的少)。Master服務器在日誌增加到必定量時對系統狀態作一次Checkpoint (alex注:Checkpoint是一種行爲,一種對數據庫狀態做一次快照的行爲),將全部的狀態數據寫入一個Checkpoint文件 (alex注:並刪除以前的日誌文件)。 在災難恢復的時候,Master服務器就經過從磁盤上讀取這個Checkpoint文件,以及重演Checkpoint以後的有限個日誌文件就可以恢復系統。Checkpoint文件以壓縮B-樹形勢的數據結構存儲,能夠直接映射到內存,在用於命名空間查詢時無需額外的解析。這大大提升了恢復速度,加強了可用性。

 

由 於建立一個Checkpoint文件須要必定的時間,因此Master服務器的內部狀態被組織爲一種格式,這種格式要確保在Checkpoint過程當中不 會阻塞正在進行的修改操做。Master服務器使用獨立的線程切換到新的日誌文件和建立新的Checkpoint文件。新的Checkpoint文件包括 切換前全部的修改。對於一個包含數百萬個文件的集羣,建立一個Checkpoint文件須要1分鐘左右的時間。建立完成後,Checkpoint文件會被 寫入在本地和遠程的硬盤裏。後端

Master服務器恢復只須要最新的Checkpoint文件和後續的日誌文件。舊的Checkpoint文件和日誌文件能夠被刪除,可是爲了應對災難性的故障(alex注:catastrophes,數據備份相關文檔中常常會遇到這個詞,表示一種超出預期範圍的災難性事件),咱們一般會多保存一些歷史文件。Checkpoint失敗不會對正確性產生任何影響,由於恢復功能的代碼能夠檢測並跳過沒有完成的Checkpoint文件。緩存

2.7 一致性模型

GFS 支持一個寬鬆的一致性模型,這個模型可以很好的支撐咱們的高度分佈的應用,同時還保持了相對簡單且容易實現的優勢。本節咱們討論GFS的一致性的保障機 制,以及對應用程序的意義。咱們也着重描述了GFS如何管理這些一致性保障機制,可是實現的細節將在本論文的其它部分討論。安全

2.7.1 GFS一致性保障機制

文件命名空間的修改(例如,文件建立)是原子性的。它們僅由Master節點的控制:命名空間鎖提供了原子性和正確性(4.1章)的保障;Master節點的操做日誌定義了這些操做在全局的順序(2.6.3章)。性能優化

 

數據修改後文件region(alex注:region這個詞用中文很是難以表達,我認爲應該是修改操做所涉及的文件中的某個範圍)的 狀態取決於操做的類型、成功與否、以及是否同步修改。表1總結了各類操做的結果。若是全部客戶端,不管從哪一個副本讀取,讀到的數據都同樣,那麼咱們認爲文 件region是「一致的」;若是對文件的數據修改以後,region是一致的,而且客戶端可以看到寫入操做所有的內容,那麼這個region是「已定義 的」。當一個數據修改操做成功執行,而且沒有受到同時執行的其它寫入操做的干擾,那麼影響的region就是已定義的(隱含了一致性):全部的客戶端均可 以看到寫入的內容。並行修改操做成功完成以後,region處於一致的、未定義的狀態:全部的客戶端看到一樣的數據,可是沒法讀到任何一次寫入操做寫入的 數據。一般狀況下,文件region內包含了來自多個修改操做的、混雜的數據片斷。失敗的修改操做致使一個region處於不一致狀態(同時也是未定義 的):不一樣的客戶在不一樣的時間會看到不一樣的數據。後面咱們將描述應用如何區分已定義和未定義的region。應用程序沒有必要再去細分未定義region 的不一樣類型。

數據修改操做分爲寫入或者記錄追加兩種。寫入操做把數據寫在應用程序指定的文件偏移位置上。即便有多個修改操做並行執行時,記錄追加操做至少能夠把數據原子性的追加到文件中一次,可是偏移位置是由GFS選擇的(3.3章) (alex注:這句話有點費解,其含義是全部的追加寫入都會成功,可是有可能被執行了屢次,並且每次追加的文件偏移量由GFS本身計算)。 (相比而言,一般說的追加操做寫的偏移位置是文件的尾部。)GFS返回給客戶端一個偏移量,表示了包含了寫入記錄的、已定義的region的起點。另 外,GFS可能會在文件中間插入填充數據或者重複記錄。這些數據佔據的文件region被認定是不一致的,這些數據一般比用戶數據小的多。

 

經 過了一系列的成功的修改操做以後,GFS確保被修改的文件region是已定義的,而且包含最後一次修改操做寫入的數據。GFS經過如下措施確保上述行 爲:(a) 對Chunk的全部副本的修改操做順序一致(3.1章),(b)使用Chunk的版本號來檢測副本是否由於它所在的Chunk服務器宕機(4.5章)而錯 過了修改操做而致使其失效。失效的副本不會再進行任何修改操做,Master服務器也再也不返回這個Chunk副本的位置信息給客戶端。它們會被垃圾收集系 統儘快回收。

因爲Chunk位置信息會被客戶端緩存,因此在信息刷新前,客戶端有可能從一個失效的副本讀取了數據。在緩存的超時時間和 文件下一次被打開的時間之間存在一個時間窗,文件再次被打開後會清除緩存中與該文件有關的全部Chunk位置信息。並且,因爲咱們的文件大多數都是隻進行 追加操做的,因此,一個失效的副本一般返回一個提早結束的Chunk而不是過時的數據。當一個Reader (alex注:本文中將用到兩個專有名詞,Reader和Writer,分別表示執行GFS讀取和寫入操做的程序)從新嘗試並聯絡Master服務器時,它就會馬上獲得最新的Chunk位置信息。

 

即 使在修改操做成功執行很長時間以後,組件的失效也可能損壞或者刪除數據。GFS經過Master服務器和全部Chunk服務器的按期「握手」來找到失效的 Chunk服務器,而且使用Checksum來校驗數據是否損壞(5.2章)。一旦發現問題,數據要儘快利用有效的副本進行恢復(4.3章)。只有當一個 Chunk的全部副本在GFS檢測到錯誤並採起應對措施以前所有丟失,這個Chunk纔會不可逆轉的丟失。在通常狀況下GFS的反應時間 (alex注:指Master節點檢測到錯誤並採起應對措施)是幾分鐘。即便在這種狀況下,Chunk也只是不可用了,而不是損壞了:應用程序會收到明確的錯誤信息而不是損壞的數據。

2.7.2 程序的實現

使用GFS的應用程序能夠利用一些簡單技術實現這個寬鬆的一致性模型,這些技術也用來實現一些其它的目標功能,包括:儘可能採用追加寫入而不是覆蓋,Checkpoint,自驗證的寫入操做,自標識的記錄。
 
在 實際應用中,咱們全部的應用程序對文件的寫入操做都是儘可能採用數據追加方式,而不是覆蓋方式。一種典型的應用,應用程序從頭至尾寫入數據,生成了一個文 件。寫入全部數據以後,應用程序自動將文件更名爲一個永久保存的文件名,或者週期性的做Checkpoint,記錄成功寫入了多少數據。 Checkpoint文件能夠包含程序級別的校驗和。Readers僅校驗並處理上個Checkpoint以後產生的文件region,這些文件 region的狀態必定是已定義的。這個方法知足了咱們一致性和併發處理的要求。追加寫入比隨機位置寫入更加有效率,對應用程序的失敗處理更具備彈性。 Checkpoint可讓Writer以漸進的方式從新開始,而且能夠防止Reader處理已經被成功寫入,可是從應用程序的角度來看還並未完成的數 據。
 
咱們再來分析另外一種典型的應用。許多應用程序並行的追加數據到同一 個文件,好比進行結果的合併或者是一個生產者-消費者隊列。記錄追加方式的「至少一次追加」的特性保證了Writer的輸出。Readers使用下面的方 法來處理偶然性的填充數據和重複內容。Writers在每條寫入的記錄中都包含了額外的信息,例如Checksum,用來驗證它的有效性。Reader可 以利用Checksum識別和拋棄額外的填充數據和記錄片斷。若是應用不能容忍偶爾的重複內容(好比,若是這些重複數據觸發了非冪等操做),能夠用記錄的 惟一標識符來過濾它們,這些惟一標識符一般用於命名程序中處理的實體對象,例如web文檔。這些記錄I/O功能 (alex注:These functionalities for record I/O)(除了剔除重複數據)都包含在咱們的程序共享的庫中,而且適用於Google內部的其它的文件接口實現。因此,相同序列的記錄,加上一些偶爾出現的重複數據,都被分發到Reader了。

3. 系統交互

咱們在設計這個系統時,一個重要的原則是最小化全部操做和Master節點的交互。帶着這樣的設計理念,咱們如今描述一下客戶機、Master服務器和Chunk服務器如何進行交互,以實現數據修改操做、原子的記錄追加操做以及快照功能。

3.1 租約(lease)和變動順序

(alex注:lease是數據庫中的一個術語)

變 更是一個會改變Chunk內容或者元數據的操做,好比寫入操做或者記錄追加操做。變動操做會在Chunk的全部副本上執行。咱們使用租約(lease)機 制來保持多個副本間變動順序的一致性。Master節點爲Chunk的一個副本創建一個租約,咱們把這個副本叫作主Chunk。主Chunk對Chunk 的全部更改操做進行序列化。全部的副本都聽從這個序列進行修改操做。所以,修改操做全局的順序首先由Master節點選擇的租約的順序決定,而後由租約中 主Chunk分配的序列號決定。

設計租約機制的目的是爲了最小化Master節點的管理負擔。租約的初始超時設置爲60秒。不過,只要 Chunk被修改了,主Chunk就能夠申請更長的租期,一般會獲得Master節點的確認並收到租約延長的時間。這些租約延長請求和批准的信息一般都是 附加在Master節點和Chunk服務器之間的心跳消息中來傳遞。有時Master節點會試圖提早取消租約(例如,Master節點想取消在一個已經被 更名的文件上的修改操做)。即便Master節點和主Chunk失去聯繫,它仍然能夠安全地在舊的租約到期後和另一個Chunk副本簽定新的租約。

 

在圖2中,咱們依據步驟編號,展示寫入操做的控制流程。

  1. 客戶機向Master節點詢問哪個Chunk服務器持有當前的租約,以及其它副本的位置。若是沒有一個Chunk持有租約,Master節點就選擇其中一個副本創建一個租約(這個步驟在圖上沒有顯示)。
  2. Master 節點將主Chunk的標識符以及其它副本(又稱爲secondary副本、二級副本)的位置返回給客戶機。客戶機緩存這些數據以便後續的操做。只有在主 Chunk不可用,或者主Chunk回覆信息代表它已再也不持有租約的時候,客戶機才須要從新跟Master節點聯繫。
  3. 客 戶機把數據推送到全部的副本上。客戶機能夠以任意的順序推送數據。Chunk服務器接收到數據並保存在它的內部LRU緩存中,一直到數據被使用或者過時交 換出去。因爲數據流的網絡傳輸負載很是高,經過分離數據流和控制流,咱們能夠基於網絡拓撲狀況對數據流進行規劃,提升系統性能,而不用去理會哪一個 Chunk服務器保存了主Chunk。3.2章節會進一步討論這點。
  4. 當全部的副本都確認接收到了數據,客戶機發 送寫請求到主Chunk服務器。這個請求標識了早前推送到全部副本的數據。主Chunk爲接收到的全部操做分配連續的序列號,這些操做可能來自不一樣的客戶 機,序列號保證了操做順序執行。它以序列號的順序把操做應用到它本身的本地狀態中 (alex注:也就是在本地執行這些操做,這句話按字面翻譯有點費解,也許應該翻譯爲「它順序執行這些操做,並更新本身的狀態」)
  5. 主Chunk把寫請求傳遞到全部的二級副本。每一個二級副本依照主Chunk分配的序列號以相同的順序執行這些操做。
  6. 全部的二級副本回復主Chunk,它們已經完成了操做。
  7. 主Chunk服務器 (alex注:即主Chunk所在的Chunk服務器)回 復客戶機。任何副本產生的任何錯誤都會返回給客戶機。在出現錯誤的狀況下,寫入操做可能在主Chunk和一些二級副本執行成功。(若是操做在主Chunk 上失敗了,操做就不會被分配序列號,也不會被傳遞。)客戶端的請求被確認爲失敗,被修改的region處於不一致的狀態。咱們的客戶機代碼經過重複執行失 敗的操做來處理這樣的錯誤。在從頭開始重複執行以前,客戶機會先從步驟(3)到步驟(7)作幾回嘗試。
 
如 果應用程序一次寫入的數據量很大,或者數據跨越了多個Chunk,GFS客戶機代碼會把它們分紅多個寫操做。這些操做都遵循前面描述的控制流程,可是可能 會被其它客戶機上同時進行的操做打斷或者覆蓋。所以,共享的文件region的尾部可能包含來自不一樣客戶機的數據片斷,儘管如此,因爲這些分解後的寫入操 做在全部的副本上都以相同的順序執行完成,Chunk的全部副本都是一致的。這使文件region處於2.7節描述的一致的、可是未定義的狀態。

 

3.2 數據流

爲 了提升網絡效率,咱們採起了把數據流和控制流分開的措施。在控制流從客戶機到主Chunk、而後再到全部二級副本的同時,數據以管道的方式,順序的沿着一 個精心選擇的Chunk服務器鏈推送。咱們的目標是充分利用每臺機器的帶寬,避免網絡瓶頸和高延時的鏈接,最小化推送全部數據的延時。
 
爲了充分利用每臺機器的帶寬,數據沿着一個Chunk服務器鏈順序的推送,而不是以其它拓撲形式分散推送(例如,樹型拓撲結構)。線性推送模式下,每臺機器全部的出口帶寬都用於以最快的速度傳輸數據,而不是在多個接受者之間分配帶寬。
 
爲 了儘量的避免出現網絡瓶頸和高延遲的連接(eg,inter-switch最有可能出現相似問題),每臺機器都儘可能的在網絡拓撲中選擇一臺尚未接收到 數據的、離本身最近的機器做爲目標推送數據。假設客戶機把數據從Chunk服務器S1推送到S4。它把數據推送到最近的Chunk服務器S1。S1把數據 推送到S2,由於S2和S4中最接近的機器是S2。一樣的,S2把數據傳遞給S3和S4之間更近的機器,依次類推推送下去。咱們的網絡拓撲很是簡單,經過 IP地址就能夠計算出節點的「距離」。
 
最後,咱們利用基於TCP鏈接 的、管道式數據推送方式來最小化延遲。Chunk服務器接收到數據後,立刻開始向前推送。管道方式的數據推送對咱們幫助很大,由於咱們採用全雙工的交換網 絡。接收到數據後馬上向前推送不會下降接收的速度。在沒有網絡擁塞的狀況下,傳送B字節的數據到R個副本的理想時間是 B/T+RL ,T是網絡的吞吐量,L是在兩臺機器數據傳輸的延遲。一般狀況下,咱們的網絡鏈接速度是100Mbps(T),L將遠小於1ms。所以,1MB的數據在理 想狀況下80ms左右就能分發出去。

3.3 原子的記錄追加

GFS 提供了一種原子的數據追加操做–記錄追加。傳統方式的寫入操做,客戶程序會指定數據寫入的偏移量。對同一個region的並行寫入操做不是串行 的:region尾部可能會包含多個不一樣客戶機寫入的數據片斷。使用記錄追加,客戶機只須要指定要寫入的數據。GFS保證至少有一次原子的寫入操做成功執 行(即寫入一個順序的byte流),寫入的數據追加到GFS指定的偏移位置上,以後GFS返回這個偏移量給客戶機。這相似於在Unix操做系統編程環境 中,對以O_APPEND模式打開的文件,多個併發寫操做在沒有競態條件時的行爲。
 
記 錄追加在咱們的分佈應用中很是頻繁的使用,在這些分佈式應用中,一般有不少的客戶機並行地對同一個文件追加寫入數據。若是咱們採用傳統方式的文件寫入操 做,客戶機須要額外的複雜、昂貴的同步機制,例如使用一個分佈式的鎖管理器。在咱們的工做中,這樣的文件一般用於多個生產者/單一消費者的隊列系統,或者 是合併了來自多個客戶機的數據的結果文件。
 
記錄追加是一種修改操做,它 也遵循3.1節描述的控制流程,除了在主Chunk有些額外的控制邏輯。客戶機把數據推送給文件最後一個Chunk的全部副本,以後發送請求給主 Chunk。主Chunk會檢查此次記錄追加操做是否會使Chunk超過最大尺寸(64MB)。若是超過了最大尺寸,主Chunk首先將當前Chunk填 充到最大尺寸,以後通知全部二級副本作一樣的操做,而後回覆客戶機要求其對下一個Chunk從新進行記錄追加操做。(記錄追加的數據大小嚴格控制在 Chunk最大尺寸的1/4,這樣即便在最壞狀況下,數據碎片的數量仍然在可控的範圍。)一般狀況下追加的記錄不超過Chunk的最大尺寸,主Chunk 把數據追加到本身的副本內,而後通知二級副本把數據寫在跟主Chunk同樣的位置上,最後回覆客戶機操做成功。
 
如 果記錄追加操做在任何一個副本上失敗了,客戶端就須要從新進行操做。從新進行記錄追加的結果是,同一個Chunk的不一樣副本可能包含不一樣的數據–重複包含 一個記錄所有或者部分的數據。GFS並不保證Chunk的全部副本在字節級別是徹底一致的。它只保證數據做爲一個總體原子的被至少寫入一次。這個特性能夠 經過簡單觀察推導出來:若是操做成功執行,數據必定已經寫入到Chunk的全部副本的相同偏移位置上。這以後,全部的副本至少都到了記錄尾部的長度,任何 後續的記錄都會追加到更大的偏移地址,或者是不一樣的Chunk上,即便其它的Chunk副本被Master節點選爲了主Chunk。就咱們的一致性保障模 型而言,記錄追加操做成功寫入數據的region是已定義的(所以也是一致的),反之則是不一致的(所以也就是未定義的)。正如咱們在2.7.2節討論 的,咱們的程序能夠處理不一致的區域。

3.4 快照

(alex注:這一節很是難以理解,總的來講依次講述了什麼是快照、快照使用的COW技術、快照如何不干擾當前操做)
快 照操做幾乎能夠瞬間完成對一個文件或者目錄樹(「源」)作一個拷貝,而且幾乎不會對正在進行的其它操做形成任何干擾。咱們的用戶能夠使用快照迅速的建立一 個巨大的數據集的分支拷貝(並且常常是遞歸的拷貝拷貝),或者是在作實驗性的數據操做以前,使用快照操做備份當前狀態,這樣以後就能夠輕鬆的提交或者回滾 到備份時的狀態。
 
就像AFS (alex注:AFS,即Andrew File System,一種分佈式文件系統),我 們用標準的copy-on-write技術實現快照。當Master節點收到一個快照請求,它首先取消做快照的文件的全部Chunk的租約。這個措施保證 了後續對這些Chunk的寫操做都必須與Master交互交互以找到租約持有者。這就給Master節點一個率先建立Chunk的新拷貝的機會。
 
租約取消或者過時以後,Master節點把這個操做以日誌的方式記錄到硬盤上。而後,Master節點經過複製源文件或者目錄的元數據的方式,把這條日誌記錄的變化反映到保存在內存的狀態中。新建立的快照文件和源文件指向徹底相同的Chunk地址。
 
在快照操做以後,當客戶機第一次想寫入數據到Chunk C,它首先會發送一個請求到Master節點查詢當前的租約持有者。Master節點注意到Chunke C的引用計數超過了1 (alex注:不太明白爲何會大於1.難道是Snapshot沒有釋放引用計數?)。 Master節點不會立刻回覆客戶機的請求,而是選擇一個新的Chunk句柄C`。以後,Master節點要求每一個擁有Chunk C當前副本的Chunk服務器建立一個叫作C`的新Chunk。經過在源Chunk所在Chunk服務器上建立新的Chunk,咱們確保數據在本地而不是 經過網絡複製(咱們的硬盤比咱們的100Mb以太網大約快3倍)。從這點來說,請求的處理方式和任何其它Chunk沒什麼不一樣:Master節點確保新 Chunk C`的一個副本擁有租約,以後回覆客戶機,客戶機獲得回覆後就能夠正常的寫這個Chunk,而沒必要理會它是從一個已存在的Chunk克隆出來的。

4. Master節點的操做

Master 節點執行全部的名稱空間操做。此外,它還管理着整個系統裏全部Chunk的副本:它決定Chunk的存儲位置,建立新Chunk和它的副本,協調各類各樣 的系統活動以保證Chunk被徹底複製,在全部的Chunk服務器之間的進行負載均衡,回收再也不使用的存儲空間。本節咱們討論上述的主題。

4.1 名稱空間管理和鎖

Master 節點的不少操做會花費很長的時間:好比,快照操做必須取消Chunk服務器上快照所涉及的全部的Chunk的租約。咱們不但願在這些操做的運行時,延緩了 其它的Master節點的操做。所以,咱們容許多個操做同時進行,使用名稱空間的region上的鎖來保證執行的正確順序。
 
不 同於許多傳統文件系統,GFS沒有針對每一個目錄實現可以列出目錄下全部文件的數據結構。GFS也不支持文件或者目錄的連接(即Unix術語中的硬連接或者 符號連接)。在邏輯上,GFS的名稱空間就是一個全路徑和元數據映射關係的查找表。利用前綴壓縮,這個表能夠高效的存儲在內存中。在存儲名稱空間的樹型結 構上,每一個節點(絕對路徑的文件名或絕對路徑的目錄名)都有一個關聯的讀寫鎖。
 
每 個Master節點的操做在開始以前都要得到一系列的鎖。一般狀況下,若是一個操做涉及/d1/d2/…/dn/leaf,那麼操做首先要得到目錄 /d1,/d1/d2,…,/d1/d2/…/dn的讀鎖,以及/d1/d2/…/dn/leaf的讀寫鎖。注意,根據操做的不一樣,leaf能夠是一個文 件,也能夠是一個目錄。
 
如今,咱們演示一下在/home/user被快 照到/save/user的時候,鎖機制如何防止建立文件/home/user/foo。快照操做獲取/home和/save的讀取鎖,以及/home /user和/save/user的寫入鎖。文件建立操做得到/home和/home/user的讀取鎖,以及/home/user/foo的寫入鎖。這 兩個操做要順序執行,由於它們試圖獲取的/home/user的鎖是相互衝突。文件建立操做不須要獲取父目錄的寫入鎖,由於這裏沒有」目錄」,或者相似 inode等用來禁止修改的數據結構。文件名的讀取鎖足以防止父目錄被刪除。
 
採 用這種鎖方案的優勢是支持對同一目錄的並行操做。好比,能夠再同一個目錄下同時建立多個文件:每個操做都獲取一個目錄名的上的讀取鎖和文件名上的寫入 鎖。目錄名的讀取鎖足以的防止目錄被刪除、更名以及被快照。文件名的寫入鎖序列化文件建立操做,確保不會屢次建立同名的文件。
 

由於名稱空間可能有不少節點,讀寫鎖採用惰性分配策略,在再也不使用的時候馬上被刪除。一樣,鎖的獲取也要依據一個全局一致的順序來避免死鎖:首先按名稱空間的層次排序,在同一個層次內按字典順序排序。

4.2 副本的位置

GFS 集羣是高度分佈的多層佈局結構,而不是平面結構。典型的拓撲結構是有數百個Chunk服務器安裝在許多機架上。Chunk服務器被來自同一或者不一樣機架上 的數百個客戶機輪流訪問。不一樣機架上的兩臺機器間的通信可能跨越一個或多個網絡交換機。另外,機架的出入帶寬可能比機架內全部機器加和在一塊兒的帶寬要小。 多層分佈架構對數據的靈活性、可靠性以及可用性方面提出特有的挑戰。
 
Chunk 副本位置選擇的策略服務兩大目標:最大化數據可靠性和可用性,最大化網絡帶寬利用率。爲了實現這兩個目的,僅僅是在多臺機器上分別存儲這些副本是不夠的, 這隻能預防硬盤損壞或者機器失效帶來的影響,以及最大化每臺機器的網絡帶寬利用率。咱們必須在多個機架間分佈儲存Chunk的副本。這保證Chunk的一 些副本在整個機架被破壞或掉線(好比,共享資源,如電源或者網絡交換機形成的問題)的狀況下依然存在且保持可用狀態。這還意味着在網絡流量方面,尤爲是針 對Chunk的讀操做,可以有效利用多個機架的整合帶寬。另外一方面,寫操做必須和多個機架上的設備進行網絡通訊,可是這個代價是咱們願意付出的。

4.3 建立,從新複製,從新負載均衡

Chunk的副本有三個用途:Chunk建立,從新複製和從新負載均衡。
 
當 Master節點建立一個Chunk時,它會選擇在哪裏放置初始的空的副本。Master節點會考慮幾個因素。(1)咱們但願在低於平均硬盤使用率的 Chunk服務器上存儲新的副本。這樣的作法最終可以平衡Chunk服務器之間的硬盤使用率。(2)咱們但願限制在每一個Chunk服務器上」最近」的 Chunk建立操做的次數。雖然建立操做自己是廉價的,可是建立操做也意味着隨之會有大量的寫入數據的操做,由於Chunk在Writer真正寫入數據的 時候才被建立,而在咱們的」追加一次,讀取屢次」的工做模式下,Chunk一旦寫入成功以後就會變爲只讀的了。(3)如上所述,咱們但願把Chunk的副 本分佈在多個機架之間。
 
當Chunk的有效副本數量少於用戶指定的複製 因數的時候,Master節點會從新複製它。這多是由幾個緣由引發的:一個Chunk服務器不可用了,Chunk服務器報告它所存儲的一個副本損壞 了,Chunk服務器的一個磁盤由於錯誤不可用了,或者Chunk副本的複製因數提升了。每一個須要被從新複製的Chunk都會根據幾個因素進行排序。一個 因素是Chunk現有副本數量和複製因數相差多少。例如,丟失兩個副本的Chunk比丟失一個副本的Chunk有更高的優先級。另外,咱們優先從新複製活 躍(live)文件的Chunk而不是最近剛被刪除的文件的Chunk(查看4.4節)。最後,爲了最小化失效的Chunk對正在運行的應用程序的影響, 咱們提升會阻塞客戶機程序處理流程的Chunk的優先級。
 
Master 節點選擇優先級最高的Chunk,而後命令某個Chunk服務器直接從可用的副本」克隆」一個副本出來。選擇新副本的位置的策略和建立時相似:平衡硬盤使 用率、限制同一臺Chunk服務器上的正在進行的克隆操做的數量、在機架間分佈副本。爲了防止克隆產生的網絡流量大大超過客戶機的流量,Master節點 對整個集羣和每一個Chunk服務器上的同時進行的克隆操做的數量都進行了限制。另外,Chunk服務器經過調節它對源Chunk服務器讀請求的頻率來限制 它用於克隆操做的帶寬。
 
最後,Master服務器週期性地對副本進行重 新負載均衡:它檢查當前的副本分佈狀況,而後移動副本以便更好的利用硬盤空間、更有效的進行負載均衡。並且在這個過程當中,Master服務器逐漸的填滿一 個新的Chunk服務器,而不是在短期內用新的Chunk填滿它,以致於過載。新副本的存儲位置選擇策略和上面討論的相同。另外,Master節點必須 選擇哪一個副本要被移走。一般狀況,Master節點移走那些剩餘空間低於平均值的Chunk服務器上的副本,從而平衡系統總體的硬盤使用率。

4.4 垃圾回收

GFS在文件刪除後不會馬上回收可用的物理空間。GFS空間回收採用惰性的策略,只在文件和Chunk級的常規垃圾收集時進行。咱們發現這個方法使系統更簡單、更可靠。

4.4.1 機制

當 一個文件被應用程序刪除時,Master節點象對待其它修改操做同樣,馬上把刪除操做以日誌的方式記錄下來。可是,Master節點並不立刻回收資源,而 是把文件名改成一個包含刪除時間戳的、隱藏的名字。當Master節點對文件系統命名空間作常規掃描的時候,它會刪除全部三天前的隱藏文件(這個時間間隔 是能夠設置的)。直到文件被真正刪除,它們仍舊能夠用新的特殊的名字讀取,也能夠經過把隱藏文件更名爲正常顯示的文件名的方式「反刪除」。當隱藏文件被從 名稱空間中刪除,Master服務器內存中保存的這個文件的相關元數據纔會被刪除。這也有效的切斷了文件和它包含的全部Chunk的鏈接 (alex注:原文是This effectively severs its links to all its chunks)
 
在 對Chunk名字空間作相似的常規掃描時,Master節點找到孤兒Chunk(不被任何文件包含的Chunk)並刪除它們的元數據。Chunk服務器在 和Master節點交互的心跳信息中,報告它擁有的Chunk子集的信息,Master節點回復Chunk服務器哪些Chunk在Master節點保存的 元數據中已經不存在了。Chunk服務器能夠任意刪除這些Chunk的副本。

4.4.2 討論

雖 然分佈式垃圾回收在編程語言領域是一個須要複雜的方案才能解決的難題,可是在GFS系統中是很是簡單的。咱們能夠輕易的獲得Chunk的全部引用:它們都 只存儲在Master服務器上的文件到塊的映射表中。咱們也能夠很輕易的獲得全部Chunk的副本:它們都以Linux文件的形式存儲在Chunk服務器 的指定目錄下。全部Master節點不能識別的副本都是」垃圾」。
 
垃圾 回收在空間回收方面相比直接刪除有幾個優點。首先,對於組件失效是常態的大規模分佈式系統,垃圾回收方式簡單可靠。Chunk可能在某些Chunk服務器 建立成功,某些Chunk服務器上建立失敗,失敗的副本處於沒法被Master節點識別的狀態。副本刪除消息可能丟失,Master節點必須從新發送失敗 的刪除消息,包括自身的和Chunk服務器的 (alex注:自身的指刪除metadata的消息)。 垃圾回收提供了一致的、可靠的清除無用副本的方法。第二,垃圾回收把存儲空間的回收操做合併到Master節點規律性的後臺活動中,好比,例行掃描和與 Chunk服務器握手等。所以,操做被批量的執行,開銷會被分散。另外,垃圾回收在Master節點相對空閒的時候完成。這樣Master節點就能夠給那 些須要快速反應的客戶機請求提供更快捷的響應。第三,延緩存儲空間回收爲意外的、不可逆轉的刪除操做提供了安全保障。
 
根 據咱們的使用經驗,延遲迴收空間的主要問題是,延遲迴收會阻礙用戶調優存儲空間的使用,特別是當存儲空間比較緊缺的時候。當應用程序重複建立和刪除臨時文 件時,釋放的存儲空間不能立刻重用。咱們經過顯式的再次刪除一個已經被刪除的文件的方式加速空間回收的速度。咱們容許用戶爲命名空間的不一樣部分設定不一樣的 複製和回收策略。例如,用戶能夠指定某些目錄樹下面的文件不作複製,刪除的文件被即時的、不可恢復的從文件系統移除。

4.5 過時失效的副本檢測

當Chunk服務器失效時,Chunk的副本有可能因錯失了一些修改操做而過時失效。Master節點保存了每一個Chunk的版本號,用來區分當前的副本和過時副本。
 
無 論什麼時候,只要Master節點和Chunk簽定一個新的租約,它就增長Chunk的版本號,而後通知最新的副本。Master節點和這些副本都把新的版本 號記錄在它們持久化存儲的狀態信息中。這個動做發生在任何客戶機獲得通知之前,所以也是對這個Chunk開始寫以前。若是某個副本所在的Chunk服務器 正好處於失效狀態,那麼副本的版本號就不會被增長。Master節點在這個Chunk服務器從新啓動,而且向Master節點報告它擁有的Chunk的集 合以及相應的版本號的時候,就會檢測出它包含過時的Chunk。若是Master節點看到一個比它記錄的版本號更高的版本號,Master節點會認爲它和 Chunk服務器簽定租約的操做失敗了,所以會選擇更高的版本號做爲當前的版本號。
 
Master 節點在例行的垃圾回收過程當中移除全部的過時失效副本。在此以前,Master節點在回覆客戶機的Chunk信息請求的時候,簡單的認爲那些過時的塊根本就 不存在。另一重保障措施是,Master節點在通知客戶機哪一個Chunk服務器持有租約、或者指示Chunk服務器從哪一個Chunk服務器進行克隆時, 消息中都附帶了Chunk的版本號。客戶機或者Chunk服務器在執行操做時都會驗證版本號以確保老是訪問當前版本的數據。

5. 容錯和診斷

我 們在設計GFS時遇到的最大挑戰之一是如何處理頻繁發生的組件失效。組件的數量和質量讓這些問題出現的頻率遠遠超過通常系統意外發生的頻率:咱們不能徹底 依賴機器的穩定性,也不能徹底相信硬盤的可靠性。組件的失效可能形成系統不可用,更糟糕的是,還可能產生不完整的數據。咱們討論咱們如何面對這些挑戰,以 及當組件失效不可避免的發生時,用GFS自帶工具診斷系統故障。

5.1 高可用性

在GFS集羣的數百個服務器之中,在任何給定的時間一定會有些服務器是不可用的。咱們使用兩條簡單可是有效的策略保證整個系統的高可用性:快速恢復和複製。

5.1.1 快速恢復

無論Master服務器和Chunk服務器是如何關閉的,它們都被設計爲能夠在數秒鐘內恢復它們的狀態並從新啓動。事實上,咱們並不區分正常關閉和異常關閉;一般,咱們經過直接kill掉進程來關閉服務器。客戶機和其它的服務器會感受到系統有點顛簸 (alex注:a minor hiccup),正在發出的請求會超時,須要從新鏈接到重啓後的服務器,而後重試這個請求。6.6.2章節記錄了實測的啓動時間。

5.1.2 Chunk複製

正 如以前討論的,每一個Chunk都被複制到不一樣機架上的不一樣的Chunk服務器上。用戶能夠爲文件命名空間的不一樣部分設定不一樣的複製級別。缺省是3。當有 Chunk服務器離線了,或者經過Chksum校驗(參考5.2節)發現了已經損壞的數據,Master節點經過克隆已有的副本保證每一個Chunk都被完 整複製(alex注:即每一個Chunk都有複製因子制定的個數個副本,缺省是3)。雖然Chunk複製策略對咱們很是有效,可是咱們也在尋找其它形式的跨服務器的冗餘解決方案,好比使用奇偶校驗、或者Erasure codes(alex注:Erasure codes用來解決連接層中不相關的錯誤,以及網絡擁塞和buffer限制形成的丟包錯誤)來解決咱們日益增加的只讀存儲需求。咱們的系統主要的工做負載是追加方式的寫入和讀取操做,不多有隨機的寫入操做,所以,咱們認爲在咱們這個高度解耦合的系統架構下實現這些複雜的冗餘方案頗有挑戰性,但並不是不可實現。

5.1.3 Master服務器的複製

爲 了保證Master服務器的可靠性,Master服務器的狀態也要複製。Master服務器全部的操做日誌和checkpoint文件都被複制到多臺機器 上。對Master服務器狀態的修改操做可以提交成功的前提是,操做日誌寫入到Master服務器的備節點和本機的磁盤。簡單說來,一個Master服務 進程負責全部的修改操做,包括後臺的服務,好比垃圾回收等改變系統內部狀態活動。當它失效的時,幾乎能夠馬上從新啓動。若是Master進程所在的機器或 者磁盤失效了,處於GFS系統外部的監控進程會在其它的存有完整操做日誌的機器上啓動一個新的Master進程。客戶端使用規範的名字訪問 Master(好比gfs-test)節點,這個名字相似DNS別名,所以也就能夠在Master進程轉到別的機器上執行時,經過更改別名的實際指向訪問 新的Master節點。

 

此外,GFS中還有些「影子」Master服務器,這些「影子」服務器在「主」Master服 務器宕機的時候提供文件系統的只讀訪問。它們是影子,而不是鏡像,因此它們的數據可能比「主」Master服務器更新要慢,一般是不到1秒。對於那些不經 常改變的文件、或者那些容許獲取的數據有少許過時的應用程序,「影子」Master服務器可以提升讀取的效率。事實上,由於文件內容是從Chunk服務器 上讀取的,所以,應用程序不會發現過時的文件內容。在這個短暫的時間窗內,過時的多是文件的元數據,好比目錄的內容或者訪問控制信息。

「影 子」Master服務器爲了保持自身狀態是最新的,它會讀取一份當前正在進行的操做的日誌副本,而且依照和主Master服務器徹底相同的順序來更改內部 的數據結構。和主Master服務器同樣,「影子」Master服務器在啓動的時候也會從Chunk服務器輪詢數據(以後按期拉數據),數據中包括了 Chunk副本的位置信息;「影子」Master服務器也會按期和Chunk服務器「握手」來肯定它們的狀態。在主Master服務器因建立和刪除副本導 致副本位置信息更新時,「影子」Master服務器才和主Master服務器通訊來更新自身狀態。

5.2 數據完整性

每 個Chunk服務器都使用Checksum來檢查保存的數據是否損壞。考慮到一個GFS集羣一般都有好幾百臺機器、幾千塊硬盤,磁盤損壞致使數據在讀寫過 程中損壞或者丟失是很是常見的(第7節講了一個緣由)。咱們能夠經過別的Chunk副原本解決數據損壞問題,可是跨越Chunk服務器比較副原本檢查數據 是否損壞很不實際。另外,GFS容許有歧義的副本存在:GFS修改操做的語義,特別是早先討論過的原子紀錄追加的操做,並不保證副本徹底相同(alex注:副本不是byte-wise徹底一致的)。所以,每一個Chunk服務器必須獨立維護Checksum來校驗本身的副本的完整性。

咱們把每一個Chunk都分紅64KB大小的塊。每一個塊都對應一個32位的Checksum。和其它元數據同樣,Checksum與其它的用戶數據是分開的,而且保存在內存和硬盤上,同時也記錄操做日誌。

對 於讀操做來講,在把數據返回給客戶端或者其它的Chunk服務器以前,Chunk服務器會校驗讀取操做涉及的範圍內的塊的Checksum。所以 Chunk服務器不會把錯誤數據傳遞到其它的機器上。若是發生某個塊的Checksum不正確,Chunk服務器返回給請求者一個錯誤信息,而且通知 Master服務器這個錯誤。做爲迴應,請求者應當從其它副本讀取數據,Master服務器也會從其它副本克隆數據進行恢復。當一個新的副本就緒 後,Master服務器通知副本錯誤的Chunk服務器刪掉錯誤的副本。

Checksum對讀操做的性能影響很小,能夠基於幾個緣由來分析 一下。由於大部分的讀操做都至少要讀取幾個塊,而咱們只須要讀取一小部分額外的相關數據進行校驗。GFS客戶端代碼經過每次把讀取操做都對齊在 Checksum block的邊界上,進一步減小了這些額外的讀取操做的負面影響。另外,在Chunk服務器上,Chunksum的查找和比較不須要I/O操 做,Checksum的計算能夠和I/O操做同時進行。

Checksum的計算針對在Chunk尾部的追加寫入操做做了高度優化(與之對應 的是覆蓋現有數據的寫入操做),由於這類操做在咱們的工做中佔了很大比例。咱們只增量更新最後一個不完整的塊的Checksum,而且用全部的追加來的新 Checksum塊來計算新的Checksum。即便是最後一個不完整的Checksum塊已經損壞了,並且咱們不可以立刻檢查出來,因爲新的 Checksum和已有數據不吻合,在下次對這個塊進行讀取操做的時候,會檢查出數據已經損壞了。

相比之下,若是寫操做覆蓋已經存在的一個 範圍內的Chunk,咱們必須讀取和校驗被覆蓋的第一個和最後一個塊,而後再執行寫操做;操做完成以後再從新計算和寫入新的Checksum。若是咱們不 校驗第一個和最後一個被寫的塊,那麼新的Checksum可能會隱藏沒有被覆蓋區域內的數據錯誤。

在Chunk服務器空閒的時候,它會掃描 和校驗每一個不活動的Chunk的內容。這使得咱們可以發現不多被讀取的Chunk是否完整。一旦發現有Chunk的數據損壞,Master能夠建立一個新 的、正確的副本,而後把損壞的副本刪除掉。這個機制也避免了非活動的、已損壞的Chunk欺騙Master節點,使Master節點認爲它們已經有了足夠 多的副本了。

5.3 診斷工具

詳 盡的、深刻細節的診斷日誌,在問題隔離、調試、以及性能分析等方面給咱們帶來沒法估量的幫助,同時也只須要很小的開銷。沒有日誌的幫助,咱們很難理解短暫 的、不重複的機器之間的消息交互。GFS的服務器會產生大量的日誌,記錄了大量關鍵的事件(好比,Chunk服務器啓動和關閉)以及全部的RPC的請求和 回覆。這些診斷日誌能夠隨意刪除,對系統的正確運行不形成任何影響。然而,咱們在存儲空間容許的狀況下會盡可能的保存這些日誌。

RPC日誌包含了網絡上發生的全部請求和響應的詳細記錄,可是不包括讀寫的文件數據。經過匹配請求與迴應,以及收集不一樣機器上的RPC日誌記錄,咱們能夠重演全部的消息交互來診斷問題。日誌還用來跟蹤負載測試和性能分析。

日誌對性能的影響很小(遠小於它帶來的好處),由於這些日誌的寫入方式是順序的、異步的。最近發生的事件日誌保存在內存中,可用於持續不斷的在線監控。

6. 度量

本節中,咱們將使用一些小規模基準測試來展示GFS系統架構和實現上的一些固有瓶頸,還有些來自Google內部使用的真實的GFS集羣的基準數據。

6.1 小規模基準測試

咱們在一個包含1臺Master服務器,2臺Master服務器複製節點,16臺Chunk服務器和16個客戶機組成的GFS集羣上測量性能。注意,採用這樣的集羣配置方案只是爲了易於測試。典型的GFS集羣有數百個Chunk服務器和數百個客戶機。

 

所 有機器的配置都同樣:兩個PIII 1.4GHz處理器,2GB內存,兩個80G/5400rpm的硬盤,以及100Mbps全雙工以太網鏈接到一個HP2524交換機。GFS集羣中全部的 19臺服務器都鏈接在一個交換機,全部16臺客戶機鏈接到另外一個交換機上。兩個交換機之間使用1Gbps的線路鏈接。

 

6.1.1 讀取

N 個客戶機從GFS文件系統同步讀取數據。每一個客戶機從320GB的文件集合中隨機讀取4MB region的內容。讀取操做重複執行256次,所以,每一個客戶機最終都讀取1GB的數據。全部的Chunk服務器加起來總共只有32GB的內存,所以, 咱們預期只有最多10%的讀取請求命中Linux的文件系統緩衝。咱們的測試結果應該和一個在沒有文件系統緩存的狀況下讀取測試的結果接近。
圖三:合計吞吐量:上邊的曲線顯示了咱們網絡拓撲下的合計理論吞吐量上限。下邊的曲線顯示了觀測到的吞吐量。這個曲線有着95%的可靠性,由於有時候測量會不夠精確。
圖 3(a)顯示了N個客戶機總體的讀取速度以及這個速度的理論極限。當鏈接兩個交換機的1Gbps的鏈路飽和時,總體讀取速度達到理論的極限值是 125MB/S,或者說每一個客戶機配置的100Mbps網卡達到飽和時,每一個客戶機讀取速度的理論極限值是12.5MB/s。實測結果是,當一個客戶機讀 取的時候,讀取的速度是10MB/s,也就是說達到客戶機理論讀取速度極限值的80%。對於16個客戶機,總體的讀取速度達到了94MB/s,大約是理論 總體讀取速度極限值的75%,也就是說每一個客戶機的讀取速度是6MB/s。讀取效率從80%下降到了75%,主要的緣由是當讀取的客戶機增長時,多個客戶 機同時讀取一個Chunk服務器的概率也增長了,致使總體的讀取效率降低。

6.1.2 寫入

N 個客戶機同時向N個不一樣的文件中寫入數據。每一個客戶機以每次1MB的速度連續寫入1GB的數據。圖3(b)顯示了總體的寫入速度和它們理論上的極限值。理 論上的極限值是67MB/s,由於咱們須要把每一byte寫入到16個Chunk服務器中的3個上,而每一個Chunk服務器的輸入鏈接速度是12.5MB /s。
 
一個客戶機的寫入速度是6.3MB,大概是理論極限值的一半。致使這個結果的主要緣由是咱們的網絡協議棧。它與咱們推送數據到Chunk服務器時採用的管道模式不相適應。從一個副本到另外一個副本的數據傳輸延遲下降了整個的寫入速度。
 
16 個客戶機總體的寫入速度達到了35MB/s(即每一個客戶機2.2MB/s),大約只是理論極限值的一半。和多個客戶機讀取的情形很類型,隨着客戶機數量的 增長,多個客戶機同時寫入同一個Chunk服務器的概率也增長了。並且,16個客戶機並行寫入可能引發的衝突比16個客戶機並行讀取要大得多,由於每一個寫 入都會涉及三個不一樣的副本。
 
寫入的速度比咱們想象的要慢。在實際應用中,這沒有成爲咱們的主要問題,由於即便在單個客戶機上可以感覺到延時,它也不會在有大量客戶機的時候對總體的寫入帶寬形成顯著的影響。

6.1.3 記錄追加

圖 3(c)顯示了記錄追加操做的性能。N個客戶機同時追加數據到一個文件。記錄追加操做的性能受限於保存文件最後一個Chunk的Chunk服務器的帶寬, 而與客戶機的數量無關。記錄追加的速度由一個客戶機的6.0MB/s開始,降低到16個客戶機的4.8MB/s爲止,速度的降低主要是因爲不一樣客戶端的網 絡擁塞以及網絡傳輸速度的不一樣而致使的。

咱們的程序傾向於同時處理多個這樣的文件。換句話說,即N個客戶機同時追加數據到M個共享文件中, 這裏N和M都是數十或者數百以上。因此,在咱們的實際應用中,Chunk服務器的網絡擁塞並無成爲一個嚴重問題,若是Chunk服務器的某個文件正在寫 入,客戶機會去寫另一個文件。

6.2 實際應用中的集羣

我 們如今來仔細評估一下Google內部正在使用的兩個集羣,它們具備必定的表明性。集羣A一般被上百個工程師用於研究和開發。典型的任務是被人工初始化後 連續運行數個小時。它一般讀取數MB到數TB的數據,以後進行轉化或者分析,最後把結果寫回到集羣中。集羣B主要用於處理當前的生產數據。集羣B的任務持 續的時間更長,在不多人工干預的狀況下,持續的生成和處理數TB的數據集。在這兩個案例中,一個單獨的」任務」都是指運行在多個機器上的多個進程,它們同 時讀取和寫入多個文件。
 

6.2.1 存儲

如 上表前五行所描述的,兩個集羣都由上百臺Chunk服務器組成,支持數TB的硬盤空間;兩個集羣雖然都存儲了大量的數據,可是還有剩餘的空間。「已用空 間」包含了全部的Chunk副本。實際上全部的文件都複製了三份。所以,集羣實際上各存儲了18TB和52TB的文件數據。

兩個集羣存儲的文件數量都差很少,可是集羣B上有大量的死文件。所謂「死文件」是指文件被刪除了或者是被新版本的文件替換了,可是存儲空間尚未來得及被回收。因爲集羣B存儲的文件較大,所以它的Chunk數量也比較多。

6.2.2 元數據

Chunk服務器總共保存了十幾GB的元數據,大多數是來自用戶數據的、64KB大小的塊的Checksum。保存在Chunk服務器上其它的元數據是Chunk的版本號信息,咱們在4.5節描述過。

在 Master服務器上保存的元數據就小的多了,大約只有數十MB,或者說平均每一個文件100字節的元數據。這和咱們設想的是同樣的,Master服務器的 內存大小在實際應用中並不會成爲GFS系統容量的瓶頸。大多數文件的元數據都是之前綴壓縮模式存放的文件名。Master服務器上存放的其它元數據包括了 文件的全部者和權限、文件到Chunk的映射關係,以及每個Chunk的當前版本號。此外,針對每個Chunk,咱們都保存了當前的副本位置以及對它 的引用計數,這個引用計數用於實現寫時拷貝(alex注:即COW,copy-on-write)。

對 於每個單獨的服務器,不管是Chunk服務器仍是Master服務器,都只保存了50MB到100MB的元數據。所以,恢復服務器是很是快速的:在服務 器響應客戶請求以前,只須要花幾秒鐘時間從磁盤上讀取這些數據就能夠了。不過,Master服務器會持續顛簸一段時間–一般是30到60秒–直到它完成輪 詢全部的Chunk服務器,並獲取到全部Chunk的位置信息。

6.2.3 讀寫速率

 
表三顯示了不一樣時段的讀寫速率。在測試的時候,這兩個集羣都運行了一週左右的時間。(這兩個集羣最近都由於升級新版本的GFS從新啓動過了)。
 
集羣從新啓動後,平均寫入速率小於30MB/s。當咱們提取性能數據的時候,集羣B正進行大量的寫入操做,寫入速度達到了100MB/s,而且由於每一個Chunk都有三個副本的緣由,網絡負載達到了300MB/s。
讀 取速率要比寫入速率高的多。正如咱們設想的那樣,總的工做負載中,讀取的比例遠遠高於寫入的比例。兩個集羣都進行着繁重的讀取操做。特別是,集羣A在一週 時間內都維持了580MB/s的讀取速度。集羣A的網絡配置能夠支持750MB/s的速度,顯然,它有效的利用了資源。集羣B支持的峯值讀取速度是 1300MB/s,可是它的應用只用到了380MB/s。 

6.2.4 Master服務器的負載

表3的數據顯示了發送到Master服務器的操做請求大概是每秒鐘200到500個。Master服務器能夠輕鬆的應付這個請求速度,因此Master服務器的處理能力不是系統的瓶頸。
 
在 早期版本的GFS中,Master服務器偶爾會成爲瓶頸。它大多數時間裏都在順序掃描某個很大的目錄(包含數萬個文件)去查找某個特定的文件。所以咱們修 改了Master服務器的數據結構,經過對名字空間進行二分查找來提升效率。如今Master服務器能夠輕鬆的每秒鐘進行數千次文件訪問。若是有須要的 話,咱們能夠經過在名稱空間數據結構以前設置名稱查詢緩衝的方式進一步提升速度。

6.2.5 恢復時間

當 某個Chunk服務器失效了,一些Chunk副本的數量可能會低於複製因子指定的數量,咱們必須經過克隆副本使Chunk副本數量達到複製因子指定的數 量。恢復全部Chunk副本所花費的時間取決於資源的數量。在咱們的試驗中,咱們把集羣B上的一個Chunk服務器Kill掉。這個Chunk服務器上大 約有15000個Chunk,共計600GB的數據。爲了減少克隆操做對正在運行的應用程序的影響,以及爲GFS調度決策提供修正空間,咱們缺省的把集羣 中併發克隆操做的數量設置爲91個(Chunk服務器的數量的40%),每一個克隆操做最多容許使用的帶寬是6.25MB/s(50mbps)。全部的 Chunk在23.2分鐘內恢復了,複製的速度高達440MB/s。

 

在另一個測試中,咱們Kill掉了兩個 Chunk服務器,每一個Chunk服務器大約有16000個Chunk,共計660GB的數據。這兩個故障致使了266個Chunk只有單個副本。這 266個Chunk被GFS優先調度進行復制,在2分鐘內恢復到至少有兩個副本;如今集羣被帶入到另一個狀態,在這個狀態下,系統能夠容忍另一個 Chunk服務器失效而不丟失數據。

6.3 工做負荷分析(Workload Breakdown)

本節中,咱們展現了對兩個GFS集羣工做負載狀況的詳細分析,這兩個集羣和6.2節中的相似,可是不徹底相同。集羣X用於研究和開發,集羣Y用於生產數據處理。 

6.3.1 方法論和注意事項

本 章節列出的這些結果數據只包括客戶機發起的原始請求,所以,這些結果可以反映咱們的應用程序對GFS文件系統產生的所有工做負載。它們不包含那些爲了實現 客戶端請求而在服務器間交互的請求,也不包含GFS內部的後臺活動相關的請求,好比前向轉發的寫操做,或者從新負載均衡等操做。

 

我 們從GFS服務器記錄的真實的RPC請求日誌中推導重建出關於IO操做的統計信息。例如,GFS客戶程序可能會把一個讀操做分紅幾個RPC請求來提升並行 度,咱們能夠經過這些RPC請求推導出原始的讀操做。由於咱們的訪問模式是高度程式化,因此咱們認爲任何不符合的數據都是偏差 (alex注:Since our access patterns are highly stylized, we expect any error to be in the noise)。應用程序若是可以記錄更詳盡的日誌,就有可能提供更準確的診斷數據;可是爲了這個目的去從新編譯和從新啓動數千個正在運行的客戶機是不現實的,並且從那麼多客戶機上收集結果也是個繁重的工做。

 

應該避免從咱們的工做負荷數據中過分的概括出廣泛的結論 (alex注:即不要把本節的數據做爲基礎的指導性數據)。由於Google徹底控制着GFS和使用GFS的應用程序,因此,應用程序都針對GFS作了優化,同時,GFS也是爲了這些應用程序而設計的。這樣的相互做用也可能存在於通常程序和文件系統中,可是在咱們的案例中這樣的做用影響可能更顯著。

6.3.2 Chunk服務器工做負荷

表4顯示了操做按涉及的數據量大小的分佈狀況。讀取操做按操做涉及的數據量大小呈現了雙峯分佈。小的讀取操做(小於64KB)通常是由查找操做的客戶端發起的,目的在於從巨大的文件中查找小塊的數據。大的讀取操做(大於512KB)通常是從頭至尾順序的讀取整個文件。

在 集羣Y上,有至關數量的讀操做沒有返回任何的數據。在咱們的應用中,尤爲是在生產系統中,常用文件做爲生產者-消費者隊列。生產者並行的向文件中追加 數據,同時,消費者從文件的尾部讀取數據。某些狀況下,消費者讀取的速度超過了生產者寫入的速度,這就會致使沒有讀到任何數據的狀況。集羣X一般用於短暫 的數據分析任務,而不是長時間運行的分佈式應用,所以,集羣X不多出現這種狀況。

寫操做按數據量大小也一樣呈現爲雙峯分佈。大的寫操做(超過256KB)一般是因爲Writer使用了緩存機制致使的。Writer緩存較小的數據,經過頻繁的Checkpoint或者同步操做,或者只是簡單的統計小的寫入(小於64KB)的數據量(alex注:即聚集屢次小的寫入操做,當數據量達到一個閾值,一次寫入),以後批量寫入

再來觀察一下記錄追加操做。咱們能夠看到集羣Y中大的記錄追加操做所佔比例比集羣X多的多,這是由於集羣Y用於咱們的生產系統,針對GFS作了更全面的調優。

 
表5顯示了按操做涉及的數據量的大小統計出來的總數據傳輸量。在全部的操做中,大的操做(超過256KB)佔據了主要的傳輸量。小的讀取(小於64KB)雖然傳輸的數據量比較少,可是在讀取的數據量中仍佔了至關的比例,這是由於在文件中隨機Seek的工做負荷而致使的。

 

6.3.3 記錄追加 vs. 寫操做

記 錄追加操做在咱們的生產系統中大量使用。對於集羣X,記錄追加操做和普通寫操做的比例按照字節比是108:1,按照操做次數比是8:1。對於做爲咱們的生 產系統的集羣Y來講,這兩個比例分別是3.7:1和2.5:1。更進一步,這一組數聽說明在咱們的兩個集羣上,記錄追加操做所佔比例都要比寫操做要大。對 於集羣X,在整個測量過程當中,記錄追加操做所佔比率都比較低,所以結果會受到一兩個使用某些特定大小的buffer的應用程序的影響。
 

如 同咱們所預期的,咱們的數據修改操做主要是記錄追加操做而不是覆蓋方式的寫操做。咱們測量了第一個副本的數據覆蓋寫的狀況。這近似於一個客戶機故意覆蓋剛 剛寫入的數據,而不是增長新的數據。對於集羣X,覆蓋寫操做在寫操做所佔據字節上的比例小於0.0001%,在所佔據操做數量上的比例小於 0.0003%。對於集羣Y,這兩個比率都是0.05%。雖然這只是某一片段的狀況,可是仍然高於咱們的預期。這是因爲這些覆蓋寫的操做,大部分是因爲客 戶端在發生錯誤或者超時之後重試的狀況。這在本質上應該不算做工做負荷的一部分,而是重試機制產生的結果。

6.3.4 Master的工做負荷

表6顯示了Master服務器上的請求按類型區分的明細表。大部分的請求都是讀取操做查詢Chunk位置信息(FindLocation)、以及修改操做查詢lease持有者的信息(FindLease-Locker)。
 
集 羣X和Y在刪除請求的數量上有着明顯的不一樣,由於集羣Y存儲了生產數據,通常會從新生成數據以及用新版本的數據替換舊有的數據。數量上的差別也被隱藏在了 Open請求中,由於舊版本的文件可能在以從新寫入的模式打開時,隱式的被刪除了(相似UNIX的open函數中的「w」模式)。
 
FindMatchingFiles 是一個模式匹配請求,支持「ls」以及其它相似的文件系統操做。不一樣於Master服務器的其它請求,它可能會檢索namespace的大部份內容,所以 是很是昂貴的操做。集羣Y的這類請求要多一些,由於自動化數據處理的任務進程須要檢查文件系統的各個部分,以便從全局上了解應用程序的狀態。與之不一樣的 是,集羣X的應用程序更加傾向於由單獨的用戶控制,一般預先知道本身所須要使用的所有文件的名稱。

7. 經驗

在建造和部署GFS的過程當中,咱們經歷了各類各樣的問題,有些是操做上的,有些是技術上的。
 
起 初,GFS被設想爲咱們的生產系統的後端文件系統。隨着時間推移,在GFS的使用中逐步的增長了對研究和開發任務的支持。咱們開始增長一些小的功能,好比 權限和配額,到了如今,GFS已經初步支持了這些功能。雖然咱們生產系統是嚴格受控的,可是用戶層卻不老是這樣的。須要更多的基礎架構來防止用戶間的相互 干擾。
 
咱們最大的問題是磁盤以及和Linux相關的問題。不少磁盤都聲 稱它們支持某個範圍內的Linux IDE硬盤驅動程序,可是實際應用中反映出來的狀況卻不是這樣,它們只支持最新的驅動。由於協議版本很接近,因此大部分磁盤均可以用,可是偶爾也會有因爲 協議不匹配,致使驅動和內核對於驅動器的狀態判斷失誤。這會致使數據由於內核中的問題意外的被破壞了。這個問題促使咱們使用Checksum來校驗數據, 同時咱們也修改內核來處理這些由於協議不匹配帶來的問題。
 
較早的時候, 咱們在使用Linux 2.2內核時遇到了些問題,主要是fsync()的效率問題。它的效率與文件的大小而不是文件修改部分的大小有關。這在咱們的操做日誌文件過大時給出了難 題,尤爲是在咱們還沒有實現Checkpoint的時候。咱們費了很大的力氣用同步寫來解決這個問題,可是最後仍是移植到了Linux2.4內核上。
 
另 一個和Linux相關的問題是單個讀寫鎖的問題,也就是說,在某一個地址空間的任意一個線程都必須在從磁盤page in(讀鎖)的時候先hold住,或者在mmap()調用(寫鎖)的時候改寫地址空間。咱們發現即便咱們的系統負載很輕的狀況下也會有偶爾的超時,咱們花 費了不少的精力去查找資源的瓶頸或者硬件的問題。最後咱們終於發現這個單個鎖在磁盤線程交換之前映射的數據到磁盤的時候,鎖住了當前的網絡線程,阻止它把 新數據映射到內存。因爲咱們的性能主要受限於網絡接口,而不是內存copy的帶寬,所以,咱們用pread()替代mmap(),用了一個額外的copy 動做來解決這個問題。
 
儘管偶爾仍是有其它的問題,Linux的開放源代碼仍是使咱們可以快速探究和理解系統的行爲。在適當的時候,咱們會改進內核而且和公開源碼組織共享這些改動。

8. 相關工做

和 其它的大型分佈式文件系統,好比AFS[5]相似,GFS提供了一個與位置無關的名字空間,這使得數據能夠爲了負載均衡或者災難冗餘等目的在不一樣位置透明 的遷移。不一樣於AFS的是,GFS把文件分佈存儲到不一樣的服務器上,這種方式更相似Xfs[1]和Swift[3],這是爲了提升總體性能以及災難冗餘的 能力。
 
因爲磁盤相對來講比較便宜,而且複製的方式比RAID[9]方法簡單的多,GFS目前只使用複製的方式來進行冗餘,所以要比xFS或者Swift佔用更多的裸存儲空間 (alex注:Raw storage,裸盤的空間)。
 
與 AFS、xFS、Frangipani[12]以及Intermezzo[6]等文件系統不一樣的是,GFS並無在文件系統層面提供任何Cache機制。 咱們主要的工做在單個應用程序執行的時候幾乎不會重複讀取數據,由於它們的工做方式要麼是流式的讀取一個大型的數據集,要麼是在大型的數據集中隨機 Seek到某個位置,以後每次讀取少許的數據。
 
某些分佈式文件系統,比 如Frangipani、xFS、Minnesota’s GFS[11]、GPFS[10],去掉了中心服務器,只依賴於分佈式算法來保證一致性和可管理性。咱們選擇了中心服務器的方法,目的是爲了簡化設計,增 加可靠性,可以靈活擴展。特別值得一提的是,因爲處於中心位置的Master服務器保存有幾乎全部的Chunk相關信息,而且控制着Chunk的全部變 更,所以,它極大地簡化了本來很是複雜的Chunk分配和複製策略的實現方法。咱們經過減小Master服務器保存的狀態信息的數量,以及將Master 服務器的狀態複製到其它節點來保證系統的災難冗餘能力。擴展能力和高可用性(對於讀取)目前是經過咱們的影子Master服務器機制來保證的。對 Master服務器狀態更改是經過預寫日誌的方式實現持久化。爲此,咱們能夠調整爲使用相似Harp[7]中的primary-copy方案,從而提供比 咱們如今的方案更嚴格的一致性保證。
 
咱們解決了一個難題,這個難題相似 Lustre[8]在如何在有大量客戶端時保障系統總體性能遇到的問題。不過,咱們經過只關注咱們的應用程序的需求,而不是提供一個兼容POSIX的文件 系統,從而達到了簡化問題的目的。此外,GFS設計預期是使用大量的不可靠節點組建集羣,所以,災難冗餘方案是咱們設計的核心。
 
GFS 很相似NASD架構[4]。NASD架構是基於網絡磁盤的,而GFS使用的是普通計算機做爲Chunk服務器,就像NASD原形中方案同樣。所不一樣的是, 咱們的Chunk服務器採用惰性分配固定大小的Chunk的方式,而不是分配變長的對象存儲空間。此外,GFS實現了諸如從新負載均衡、複製、恢復機制等 等在生產環境中須要的特性。
 
不一樣於與Minnesota’s GFS和NASD,咱們並不改變存儲設備的Model (alex注:對這兩個文件系統不瞭解,由於不太明白改變存儲設備的Model用來作什麼,這不明白這個model是模型、仍是型號)。咱們只關注用普通的設備來解決很是複雜的分佈式系統平常的數據處理。
 
我 們經過原子的記錄追加操做實現了生產者-消費者隊列,這個問題相似River[2]中的分佈式隊列。River使用的是跨主機的、基於內存的分佈式隊列, 爲了實現這個隊列,必須仔細控制數據流;而GFS採用能夠被生產者併發追加記錄的持久化的文件的方式實現。River模式支持m-到-n的分佈式隊列,但 是缺乏由持久化存儲提供的容錯機制,GFS只支持m-到-1的隊列。多個消費者能夠同時讀取一個文件,可是它們輸入流的區間必須是對齊的。

9. 結束語

Google文件系統展現了一個使用普通硬件支持大規模數據處理的系統的特質。雖然一些設計要點都是針對咱們的特殊的須要定製的,可是仍是有不少特性適用於相似規模的和成本的數據處理任務。
 
首 先,咱們根據咱們當前的和可預期的未來的應用規模和技術環境來評估傳統的文件系統的特性。咱們的評估結果將咱們引導到一個使用徹底不一樣於傳統的設計思路 上。根據咱們的設計思路,咱們認爲組件失效是常態而不是異常,針對採用追加方式(有多是併發追加)寫入、而後再讀取(一般序列化讀取)的大文件進行優 化,以及擴展標準文件系統接口、放鬆接口限制來改進整個系統。
 
我 們系統經過持續監控,複製關鍵數據,快速和自動恢復提供災難冗餘。Chunk複製使得咱們能夠對Chunk服務器的失效進行容錯。高頻率的組件失效要求系 統具有在線修復機制,可以週期性的、透明的修復損壞的數據,也可以第一時間從新創建丟失的副本。此外,咱們使用Checksum在磁盤或者IDE子系統級 別檢測數據損壞,在這樣磁盤數量驚人的大系統中,損壞率是至關高的。
 
咱們的設計保證了在有大量的併發讀寫操做時可以提供很高的合計吞吐量。咱們經過分離控制流和數據流來實現這個目標,控制流在Master服務器處理,而數據流在Chunk服務器和客戶端處理。當通常的操做涉及到Master服務器時,因爲GFS選擇的Chunk尺寸較大(alex注:從而減少了元數據的大小)以及經過Chunk Lease將控制權限移交給主副本,這些措施將Master服務器的負擔降到最低。這使得一個簡單、中心的Master不會成爲成爲瓶頸。咱們相信咱們對網絡協議棧的優化能夠提高當前對於每客戶端的寫入吞吐量限制。
 
GFS成功的實現了咱們對存儲的需求,在Google內部,不管是做爲研究和開發的存儲平臺,仍是做爲生產系統的數據處理平臺,都獲得了普遍的應用。它是咱們持續創新和處理整個WEB範圍內的難題的一個重要工具。
相關文章
相關標籤/搜索