Google File System- [GFS]·中譯本
Google檔案系統
GFS是一個可擴充套件的分散式檔案系統,用於大型的、分散式的、對大量資料進行訪問的應用。它運行於廉價的普通硬體上,但可以提供容錯功能。它可以給大量的使用者提供總體效能較高的服務。
1、設計概覽
(1)設計想定
GFS與過去的分散式檔案系統有很多相同的目標,但GFS的設計受到了當前及預期的應用方面的工作量及技術環境的驅動,這反映了它與早期的檔案系統明顯不同的設想。這就需要對傳統的選擇進行重新檢驗並進行完全不同的設計觀點的探索。
GFS與以往的檔案系統的不同的觀點如下:
1、部件錯誤不再被當作異常,而是將其作為常見的情況加以處理。因為檔案系統由成百上千個用於儲存的機器構成,而這些機器是由廉價的普通部件組成並被大量的客戶機訪問。部件的數量和質量使得一些機器隨時都有可能無法工作並且有一部分還可能無法恢復。所以實時地監控、錯誤檢測、容錯、自動恢復對系統來說必不可少。
2、按照傳統的標準,檔案都非常大。長度達幾個GB的檔案是很平常的。每個檔案通常包含很多應用物件。當經常要處理快速增長的、包含數以萬計的物件、長度達TB的資料集時,我們很難管理成千上萬的KB規模的檔案塊,即使底層檔案系統提供支援。因此,設計中操作的引數、塊的大小必須要重新考慮。對大型的檔案的管理一定要能做到高效,對小型的檔案也必須支援,但不必優化。
3、大部分檔案的更新是通過新增新資料完成的,而不是改變已存在的資料。在一個檔案中隨機的操作在實踐中幾乎不存在。一旦寫完,檔案就只可讀,很多資料都有這些特性。一些資料可能組成一個大倉庫以供資料分析程式掃描。有些是執行中的程式連續產生的資料流。有些是檔案性質的資料,有些是在某個機器上產生、在另外一個機器上處理的中間資料。由於這些對大型檔案的訪問方式,新增操作成為效能優化和原子性保證的焦點。而在客戶機中快取資料塊則失去了吸引力。
4、工作量主要由兩種讀操作構成:對大量資料的流方式的讀操作和對少量資料的隨機方式的讀操作。在前一種讀操作中,可能要讀幾百KB,通常達 1MB和更多。來自同一個客戶的連續操作通常會讀檔案的一個連續的區域。隨機的讀操作通常在一個隨機的偏移處讀幾個KB。效能敏感的應用程式通常將對少量資料的讀操作進行分類並進行批處理以使得讀操作穩定地向前推進,而不要讓它來來回回的讀。
5、工作量還包含許多對大量資料進行的、連續的、向檔案新增資料的寫操作。所寫的資料的規模和讀相似。一旦寫完,檔案很少改動。在隨機位置對少量資料的寫操作也支援,但不必非常高效。
6、系統必須高效地實現定義完好的大量客戶同時向同一個檔案的新增操作的語義。
(2)系統介面
GFS提供了一個相似地檔案系統介面,雖然它沒有向POSIX那樣實現標準的API。檔案在目錄中按層次組織起來並由路徑名標識。
(3)體系結構:
一個GFS叢集由一個master和大量的chunkserver構成,並被許多客戶(Client)訪問。如圖1所示。Master和 chunkserver通常是執行使用者層服務程序的Linux機器。只要資源和可靠性允許,chunkserver和client可以執行在同一個機器上。
檔案被分成固定大小的塊。每個塊由一個不變的、全域性唯一的64位的chunk-handle標識,chunk-handle是在塊建立時由 master分配的。ChunkServer將塊當作Linux檔案儲存在本地磁碟並可以讀和寫由chunk-handle和位區間指定的資料。出於可靠性考慮,每一個塊被複制到多個chunkserver上。預設情況下,儲存3個副本,但這可以由使用者指定。
Master維護檔案系統所以的元資料(metadata),包括名字空間、訪問控制資訊、從檔案到塊的對映以及塊的當前位置。它也控制系統範圍的活動,如塊租約(lease)管理,孤兒塊的垃圾收集,chunkserver間的塊遷移。Master定期通過HeartBeat訊息與每一個 chunkserver通訊,給chunkserver傳遞指令並收集它的狀態。
與每個應用相聯的GFS客戶程式碼實現了檔案系統的API並與master和chunkserver通訊以代表應用程式讀和寫資料。客戶與master的交換隻限於對元資料(metadata)的操作,所有資料方面的通訊都直接和chunkserver聯絡。
客戶和chunkserver都不快取檔案資料。因為使用者快取的益處微乎其微,這是由於資料太多或工作集太大而無法快取。不快取資料簡化了客戶程式和整個系統,因為不必考慮快取的一致性問題。但使用者快取元資料(metadata)。Chunkserver也不必快取檔案,因為塊時作為本地檔案儲存的。
(4)單master。
只有一個master也極大的簡化了設計並使得master可以根據全域性情況作出先進的塊放置和複製決定。但是我們必須要將master對讀和寫的參與減至最少,這樣它才不會成為系統的瓶頸。Client從來不會從master讀和寫檔案資料。Client只是詢問master它應該和哪個 chunkserver聯絡。Client在一段限定的時間內將這些資訊快取,在後續的操作中Client直接和chunkserver互動。
以圖1解釋一下一個簡單的讀操作的互動。
1、client使用固定的塊大小將應用程式指定的檔名和位元組偏移轉換成檔案的一個塊索引(chunk index)。
2、給master傳送一個包含檔名和塊索引的請求。
3、master迴應對應的chunk handle和副本的位置(多個副本)。
4、client以檔名和塊索引為鍵快取這些資訊。(handle和副本的位置)。
5、Client 向其中一個副本傳送一個請求,很可能是最近的一個副本。請求指定了chunk handle(chunkserver以chunk handle標識chunk)和塊內的一個位元組區間。
6、除非快取的資訊不再有效(cache for a limited time)或檔案被重新開啟,否則以後對同一個塊的讀操作不再需要client和master間的互動。
通常Client可以在一個請求中詢問多個chunk的地址,而master也可以很快回應這些請求。
(5)塊規模:
塊規模是設計中的一個關鍵引數。我們選擇的是64MB,這比一般的檔案系統的塊規模要大的多。每個塊的副本作為一個普通的Linux檔案儲存,在需要的時候可以擴充套件。
塊規模較大的好處有:
1、減少client和master之間的互動。因為讀寫同一個塊只是要在開始時向master請求塊位置資訊。對於讀寫大型檔案這種減少尤為重要。即使對於訪問少量資料的隨機讀操作也可以很方便的為一個規模達幾個TB的工作集緩快取塊位置資訊。
2、Client在一個給定的塊上很可能執行多個操作,和一個chunkserver保持較長時間的TCP連線可以減少網路負載。
3、這減少了master上儲存的元資料(metadata)的規模,從而使得可以將metadata放在記憶體中。這又會帶來一些別的好處。
不利的一面:
一個小檔案可能只包含一個塊,如果很多Client訪問改檔案的話,儲存這些塊的chunkserver將成為訪問的熱點。但在實際應用中,應用程式通常順序地讀包含多個塊的檔案,所以這不是一個主要問題。
(6)元資料(metadata):
master儲存了三中型別的metadata:檔案的名字空間和塊的名字空間,從檔案到塊的對映,塊的副本的位置。所有的metadata都放在記憶體中。前兩種型別的metadata通過向操作日誌登記修改而保持不變,操作日誌儲存在master的本地磁碟並在幾個遠端機器上留有副本。使用日誌使得我們可以很簡單地、可靠地更新master的狀態,即使在master崩潰的情況下也不會有不一致的問題。相反,mater在每次啟動以及當有 chuankserver加入的時候詢問每個chunkserver的所擁有的塊的情況。
A、記憶體資料結構:
因為metadata儲存在記憶體中,所以master的操作很快。進一步,master可以輕易而且高效地定期在後臺掃描它的整個狀態。這種定期地掃描被用於實現塊垃圾收集、chunkserver出現故障時的副本複製、為平衡負載和磁碟空間而進行的塊遷移。
這種方法的一個潛在的問題就是塊的數量也即整個系統的容量是否受限與master的記憶體。實際上,這並不是一個嚴重的問題。Master為每個 64MB的塊維護的metadata不足64個位元組。除了最後一塊,檔案所有的塊都是滿的。類似的,每個檔案的名字空間資料也不足64個位元組,因為檔名是以一種事先確定的壓縮方式儲存的.如果要支援更大的檔案系統,那麼增加一些記憶體的方法對於我們將元資料(metadata)儲存在記憶體種所獲得的簡單性、可靠性、高效能和靈活性來說,這只是一個很小的代價。
B、塊位置:
master並不為chunkserver所擁有的塊的副本的儲存一個不變的記錄。它在啟動時通過簡單的查詢來獲得這些資訊。Master可以保持這些資訊的更新,因為它控制所有塊的放置並通過HeartBeat訊息來監控chunkserver的狀態。
這樣做的好處:因為chunkserver可能加入或離開叢集、改變路徑名、崩潰、重啟等,一個叢集重有成百個server,這些事件經常發生,這種方法就排除了master與chunkserver之間的同步問題。
另一個原因是:只有chunkserver才能確定它自己到底有哪些塊,由於錯誤,chunkserver中的一些塊可能會很自然的消失,這樣在master中就沒有必要為此儲存一個不變的記錄。
C、操作日誌:
操作日誌包含了對metadata所作的修改的歷史記錄。它作為邏輯時間線定義了併發操作的執行順序。檔案、塊以及它們的版本號都由它們被建立時的邏輯時間而唯一地、永久地被標識。
操作日誌是如此的重要,我們必須要將它可靠地儲存起來,並且只有在metadata的改變固定下來之後才將變化呈現給使用者。所以我們將操作日誌複製到數個遠端的機器上,並且只有在將相應的日誌記錄寫到本地和遠端的磁碟上之後才回答使用者的請求。
Master可以用操作日誌來恢復它的檔案系統的狀態。為了將啟動時間減至最小,日誌就必須要比較小。每當日誌的長度增長到超過一定的規模後,master就要檢查它的狀態,它可以從本地磁碟裝入最近的檢查點來恢復狀態。
建立一個檢查點比較費時,master的內部狀態是以一種在建立一個檢查點時並不耽誤即將到來的修改操作的方式來組織的。Master切換到一個新的日子檔案並在一個單獨的執行緒中建立檢查點。這個新的檢查點記錄了切換前所有的修改。在一個有數十萬檔案的叢集中用一分鐘左右就能完成。建立完後,將它寫入本地和遠端的磁碟。
(7)資料完整性
名字空間的修改必須是原子性的,它們只能有master處理:名字空間鎖保證了操作的原子性和正確性,而master的操作日誌在全域性範圍內定義了這些操作的順序。
檔案區間的狀態在修改之後依賴於修改的型別,不論操作成功還是失敗,也不論是不是併發操作。如果不論從哪個副本上讀,所有的客戶都看到同樣的資料,那麼檔案的這個區域就是一致的。如果檔案的區域是一致的並且使用者可以看到修改操作所寫的資料,那麼它就是已定義的。如果修改是在沒有併發寫操作的影響下完成的,那麼受影響的區域是已定義的,所有的client都能看到寫的內容。成功的併發寫操作是未定義但卻是一致的。失敗的修改將使區間處於不一致的狀態。
Write操作在應用程式指定的偏移處寫入資料,而record append操作使得資料(記錄)即使在有併發修改操作的情況下也至少原子性的被加到GFS指定的偏移處,偏移地址被返回給使用者。
在一系列成功的修改操作後,最後的修改操作保證檔案區域是已定義的。GFS通過對所有的副本執行同樣順序的修改操作並且使用塊版本號檢測過時的副本(由於chunkserver退出而導致丟失修改)來做到這一點。
因為使用者快取了會位置資訊,所以在更新快取之前有可能從一個過時的副本中讀取資料。但這有快取的截止時間和檔案的重新開啟而受到限制。
在修改操作成功後,部件故障仍可以是資料受到破壞。GFS通過master和chunkserver間定期的handshake,藉助校驗和來檢測對資料的破壞。一旦檢測到,就從一個有效的副本儘快重新儲存。只有在GFS檢測前,所有的副本都失效,這個塊才會丟失。
2、系統互動
(1)租約(lease)和修改順序:
(2)資料流
我們的目標是充分利用每個機器的網路頻寬,避免網路瓶頸和延遲
為了有效的利用網路,我們將資料流和控制流分離。資料是以流水線的方式在選定的chunkerserver鏈上線性的傳遞的。每個機器的整個對外頻寬都被用作傳遞資料。為避免瓶頸,每個機器在收到資料後,將它收到資料儘快傳遞給離它最近的機器。
(3)原子性的record Append:
GFS提供了一個原子性的新增操作:record append。在傳統的寫操作中,client指定被寫資料的偏移位置,向同一個區間的併發的寫操作是不連續的:區間有可能包含來自多個client的資料碎片。在record append中, client只是指定資料。GFS在其選定的偏移出將資料至少原子性的加入檔案一次,並將偏移返回給client。
在分散式的應用中,不同機器上的許多client可能會同時向一個檔案執行新增操作,新增操作被頻繁使用。如果用傳統的write操作,可能需要額外的、複雜的、開銷較大的同步,例如通過分散式鎖管理。在我們的工作量中,這些檔案通常以多個生產者單個消費者佇列的方式或包含從多個不同 client的綜合結果。
Record append和前面講的write操作的控制流差不多,只是在primary上多了一些邏輯判斷。首先,client將資料傳送到檔案最後一塊的所有副本上。然後向primary傳送請求。Primary檢查新增操作是否會導致該塊超過最大的規模(64M)。如果這樣,它將該塊擴充到最大規模,並告訴其它副本做同樣的事,同時通知client該操作需要在下一個塊上重新嘗試。如果記錄滿足最大規模的要求,primary就會將資料新增到它的副本上,並告訴其它的副本在在同樣的偏移處寫資料,最後primary向client報告寫操作成功。如果在任何一個副本上record append操作失敗,client將重新嘗試該操作。這時候,同一個塊的副本可能包含不同的資料,因為有的可能複製了全部的資料,有的可能只複製了部分。GFS不能保證所有的副本每個位元組都是一樣的。它只保證每個資料作為一個原子單元被寫過至少一次。這個是這樣得出的:操作要是成功,資料必須在所有的副本上的同樣的偏移處被寫過。進一步,從這以後,所有的副本至少和記錄一樣長,所以後續的記錄將被指定到更高的偏移處或者一個不同的塊上,即使另一個副本成了primary。根據一致性保證,成功的record append操作的區間是已定義的。而受到干擾的區間是不一致的。
(4)快照(snapshot)
快照操作幾乎在瞬間構造一個檔案和目錄樹的副本,同時將正在進行的其他修改操作對它的影響減至最小。
我們使用copy-on-write技術來實現snapshot。當master受到一個snapshot請求時,它首先將要snapshot的檔案上塊上的lease。這使得任何一個向這些塊寫資料的操作都必須和master互動以找到擁有lease的副本。這就給master一個建立這個塊的副本的機會。
副本被撤銷或終止後,master在磁碟上登記執行的操作,然後複製原始檔或目錄樹的metadata以對它的記憶體狀態實施登記的操作。這個新建立的snapshot檔案和原始檔(其metadata)指向相同的塊(chunk)。
Snapshot之後,客戶第一次向chunk c寫的時候,它發一個請求給master以找到擁有lease的副本。Master注意到chunk c的引用記數比1大,它延遲對使用者的響應,選擇一個chunk handle C’,然後要求每一有chunk c的副本的chunkserver建立一個塊C’。每個chunkserver在本地建立chunk C’避免了網路開銷。從這以後和對別的塊的操作沒有什麼區別。
3、MASTER操作
MASTER執行所有名字空間的操作,除此之外,他還在系統範圍管理資料塊的複製:決定資料塊的放置方案,產生新資料塊並將其備份,和其他系統範圍的操作協同來確保資料備份的完整性,在所有的資料塊伺服器之間平衡負載並收回沒有使用的儲存空間。
3.1 名字空間管理和加鎖
與傳統檔案系統不同的是,GFS沒有與每個目錄相關的能列出其所有檔案的資料結構,它也不支援別名(unix中的硬連線或符號連線),不管是對檔案或是目錄。GFS的名字空間邏輯上是從檔案元資料到路徑名對映的一個查用表。
MASTER在執行某個操作前都要獲得一系列鎖,例如,它要對/d1/d2…/dn/leaf執行操作,則它必須獲得/d1,/d1/d2,…, /d1/d2/…/dn的讀鎖,/d1/d2…/dn/leaf的讀鎖或寫鎖(其中leaf可以使檔案也可以是目錄)。MASTER操作的並行性和資料的一致性就是通過這些鎖來實現的。
3.2 備份儲存放置策略
一個GFS叢集檔案系統可能是多層分佈的。一般情況下是成千上萬個檔案塊伺服器分佈於不同的機架上,而這些檔案塊伺服器又被分佈於不同機架上的客戶來訪問。因此,不同機架上的兩臺機器之間的通訊可能通過一個或多個交換機。資料塊冗餘配置策略要達到連個目的:最大的資料可靠性和可用性,最大的網路頻寬利用率。因此,如果僅僅把資料的拷貝置於不同的機器上很難滿足這兩個要求,必須在不同的機架上進行資料備份。這樣即使整個機架被毀或是掉線,也能確保資料的正常使用。這也使資料傳輸,尤其是讀資料,可以充分利用頻寬,訪問到多個機架,而寫操作,則不得不涉及到更多的機架。
3.3 產生、重複制、重平衡資料塊
當MASTER產生新的資料塊時,如何放置新資料塊,要考慮如下幾個因素:(1)儘量放置在磁碟利用率低的資料塊伺服器上,這樣,慢慢地各伺服器的磁碟利用率就會達到平衡。(2)儘量控制在一個伺服器上的“新建立”的次數。(3)由於上一小節討論的原因,我們需要把資料塊放置於不同的機架上。
MASTER在可用的資料塊備份低於使用者設定的數目時需要進行重複制。這種情況源於多種原因:伺服器不可用,資料被破壞,磁碟被破壞,或者備份數目被修改。每個被需要重複制的資料塊的優先順序根據以下幾項確定:第一是現在的數目距目標的距離,對於能阻塞使用者程式的資料塊,我們也提高它的優先順序。最後, MASTER按照產生資料塊的原則複製資料塊,並把它們放到不同的機架內的伺服器上。
MASTER週期性的平衡各伺服器上的負載:它檢查chunk分佈和負載平衡,通過這種方式來填充一個新的伺服器而不是把其他的內容統統放置到它上面帶來大量的寫資料。資料塊放置的原則與上面討論的相同,此外,MASTER還決定那些資料塊要被移除,原則上他會清除那些空閒空間低於平均值的那些伺服器。
3.4 垃圾收集
在一個檔案被刪除之後,GFS並不立即收回磁碟空間,而是等到垃圾收集程式在檔案和資料塊級的的檢查中收回。
當一個檔案被應用程式刪除之後,MASTER會立即記錄下這些變化,但檔案所佔用的資源卻不會被立即收回,而是重新給檔案命了一個隱藏的名字,並附上了刪除的時間戳。在MASTER定期檢查名字空間時,它刪除超過三天(可以設定)的隱藏的檔案。在此之前,可以以一個新的名字來讀檔案,還可以以前的名字恢復。當隱藏的檔案在名字空間中被刪除以後,它在記憶體中的元資料即被擦除,這就有效地切斷了他和所有資料塊的聯絡。
在一個相似的定期的名字空間檢查中,MASTER確認孤兒資料塊(不屬於任何檔案)並擦除他的元資料,在和MASTER的心跳資訊交換中,每個伺服器報告他所擁有的資料塊,MASTER返回元資料不在記憶體的資料塊,伺服器即可以刪除這些資料塊。
3.5 過時資料的探測
在資料更新時如果伺服器停機了,那麼他所儲存的資料備份就會過時。對每個資料塊,MASTER設定了一個版本號來區別更新過的資料塊和過時的資料塊。
當MASTER授權一個新的lease時,他會增加資料塊的版本號並會通知更新資料備份。MASTER和備份都會記錄下當前的版本號,如果一個備份當時不可用,那麼他的版本號不可能提高,當ChunkServer重新啟動並向MASTER報告他的資料塊集時,MASTER就會發現過時的資料。
MASTER在定期的垃圾收集程式中清除過時的備份,在此以前,處於效率考慮,在各客戶及英大使,他會認為根本不存在過時的資料。作為另一個安全措施, MASTER在給客戶及關於資料塊的應答或是另外一個讀取資料的伺服器資料是都會帶上版本資訊,在操作前客戶機和伺服器會驗證版本資訊以確保得到的是最新的資料。
4、容錯和診斷
4.1 高可靠性
4.1.1 快速恢復
不管如何終止服務,MASTER和資料塊伺服器都會在幾秒鐘內恢復狀態和執行。實際上,我們不對正常終止和不正常終止進行區分,伺服器程序都會被切斷而終止。客戶機和其他的伺服器會經歷一個小小的中斷,然後它們的特定請求超時,重新連線重啟的伺服器,重新請求。
4.1.2 資料塊備份
如上文所討論的,每個資料塊都會被備份到放到不同機架上的不同伺服器上。對不同的名字空間,使用者可以設定不同的備份級別。在資料塊伺服器掉線或是資料被破壞時,MASTER會按照需要來複制資料塊。
4.1.3 MASTER備份
為確保可靠性,MASTER的狀態、操作記錄和檢查點都在多臺機器上進行了備份。一個操作只有在資料塊伺服器硬碟上重新整理並被記錄在MASTER和其備份的上之後才算是成功的。如果MASTER或是硬碟失敗,系統監視器會發現並通過改變域名啟動它的一個備份機,而客戶機則僅僅是使用規範的名稱來訪問,並不會發現MASTER的改變。
4.2 資料完整性
每個資料塊伺服器都利用校驗和來檢驗儲存資料的完整性。原因:每個伺服器隨時都有發生崩潰的可能性,並且在兩個伺服器間比較資料塊也是不現實的,同時,在兩臺伺服器間拷貝資料並不能保證資料的一致性。
每個Chunk按64kB的大小分成塊,每個塊有32位的校驗和,校驗和和日誌儲存在一起,和使用者資料分開。
在讀資料時,伺服器首先檢查與被讀內容相關部分的校驗和,因此,伺服器不會傳播錯誤的資料。如果所檢查的內容和校驗和不符,伺服器就會給資料請求者返回一個錯誤的資訊,並把這個情況報告給MASTER。客戶機就會讀其他的伺服器來獲取資料,而MASTER則會從其他的拷貝來複制資料,等到一個新的拷貝完成時,MASTER就會通知報告錯誤的伺服器刪除出錯的資料塊。
附加寫資料時的校驗和計算優化了,因為這是主要的寫操作。我們只是更新增加部分的校驗和,即使末尾部分的校驗和資料已被損壞而我們沒有檢查出來,新的校驗和與資料會不相符,這種衝突在下次使用時將會被檢查出來。
相反,如果是覆蓋現有資料的寫,在寫以前,我們必須檢查第一和最後一個數據塊,然後才能執行寫操作,最後計算和記錄校驗和。如果我們在覆蓋以前不先檢查首位資料塊,計算出的校驗和則會因為沒被覆蓋的資料而產生錯誤。
在空閒時間,伺服器會檢查不活躍的資料塊的校驗和,這樣可以檢查出不經常讀的資料的錯誤。一旦錯誤被檢查出來,伺服器會拷貝一個正確的資料塊來代替錯誤的。
4.3 診斷工具
廣泛而細緻的診斷日誌以微小的代價換取了在問題隔離、診斷、效能分析方面起到了重大的作用。GFS伺服器用日誌來記錄顯著的事件(例如伺服器停機和啟動)和遠端的應答。遠端日誌記錄機器之間的請求和應答,通過收集不同機器上的日誌記錄,並對它們進行分析恢復,我們可以完整地重現活動的場景,並用此來進行錯誤分析。
6 測量
6.1 測試環境
一臺主控機,兩臺主控機備份,16臺數據塊伺服器,16臺客戶機。
每臺機器:2塊PIII1.4G處理器,2G記憶體,2塊80G5400rpm的硬碟,1塊100Mbps全雙工網絡卡
19臺伺服器連線到一個HP2524交換機上,16臺客戶機倆接到領外一臺交換機上,兩臺交換機通過1G的鏈路相連。