- 論壇徽章:
- 0
|
<DIV>
<P style="TEXT-INDENT: 2em"><A href="http://blog.chinaunix.nethttp://blog.chinaunix.net/attachment/201101/20/25098298_1295515749f0m1.png" target=_blank></A>摘要</P>
<P style="TEXT-INDENT: 2em">我們設(shè)計實現(xiàn)了google文件系統(tǒng),一個面向大規(guī)模分布式數(shù)據(jù)密集性應(yīng)用的可擴展分布式文件系統(tǒng)。它運行在廉價的商品化硬件上提供容錯功能,為大量的客戶端提供高的整體性能。 </P>
<P style="TEXT-INDENT: 2em">盡管與現(xiàn)有的分布式文件系統(tǒng)具有很多相同的目標,我們的設(shè)計更多的來源于對于我們的具體應(yīng)用的負載類型以及當(dāng)前甚至未來技術(shù)環(huán)境的觀察,這就使得它與早期的文件系統(tǒng)表現(xiàn)出明顯的不同。這也使得我們重新審視傳統(tǒng)上的設(shè)計選擇,探索出一些在根本上不同的設(shè)計觀點。</P>
<P style="TEXT-INDENT: 2em"> 這個文件系統(tǒng)成功的滿足了我們的存儲需求。伴隨這研究和開發(fā)的努力,在google內(nèi)部,它已經(jīng)作為那些需要大數(shù)據(jù)集服務(wù)的數(shù)據(jù)生成處理的基礎(chǔ)存儲平臺而廣泛部署。迄今為止,最大的集群可以通過超過一千臺機器的數(shù)千塊硬盤提供數(shù)百T的存儲,這些存儲空間可以由數(shù)百個客戶端并發(fā)訪問。</P>
<P style="TEXT-INDENT: 2em"> 在本論文中,我們將描述為了支持分布式應(yīng)用的文件系統(tǒng)擴展接口設(shè)計,討論很多我們的設(shè)計觀點,展示來自于beachmark和現(xiàn)實世界的一些測量數(shù)據(jù)。</P>
<P style="TEXT-INDENT: 2em"> 分類和主題描述:分布式文件系統(tǒng) </P>
<P style="TEXT-INDENT: 2em"> 常用詞:設(shè)計,可靠性,性能,測量</P>
<P style="TEXT-INDENT: 2em"> 關(guān)鍵詞:容錯,可擴展,數(shù)據(jù)存儲,集群存儲</P>
<P style="TEXT-INDENT: 2em"> 導(dǎo)引</P>
<P style="TEXT-INDENT: 2em">為了滿足google快速增長的數(shù)據(jù)處理需求,我們設(shè)計實現(xiàn)了google文件系統(tǒng)(GFS)。GFS與傳統(tǒng)的分布式文件系統(tǒng)具有很多相同的目標比如性能,可擴展性,可靠性,可用性。然而,它的設(shè)計是由我們的具體應(yīng)用的負載類型以及當(dāng)前甚至未來技術(shù)環(huán)境的觀察驅(qū)動的,所以與早期文件系統(tǒng)的設(shè)計假設(shè)具有明顯的區(qū)別。我們重新審視傳統(tǒng)上的設(shè)計選擇,探索出一些在根本上不同的設(shè)計觀點。</P>
<P style="TEXT-INDENT: 2em"> 一,組件失敗成為一種常態(tài)而不是異常。文件系統(tǒng)是由成百上千臺通過廉價的商品化部件組裝起來的存儲機器構(gòu)成,可以被大量的客戶端訪問。組件的數(shù)量和質(zhì)量在本質(zhì)上決定了在某一時間有一些是不可用的,有一些無法從當(dāng)前的失敗中恢復(fù)過來。我們觀察到,應(yīng)用程序的bug,操作系統(tǒng)bug,人為的錯誤,硬盤的失敗,內(nèi)存,連接器,網(wǎng)絡(luò),電力供應(yīng)都可以引起這樣的問題。因此經(jīng)常性的監(jiān)控,錯誤檢測,容錯和自動恢復(fù)必須集成到系統(tǒng)中。</P>
<P style="TEXT-INDENT: 2em"> 二,與傳統(tǒng)的標準相比,文件是巨大的。在這里,好幾個G的文件是很普通的。每個文件通常包含很多的應(yīng)用程序處理的對象比如網(wǎng)頁文檔。當(dāng)我們?nèi)粘L幚淼目焖僭鲩L的數(shù)據(jù)集合總是達到好幾個TB的大小,包含數(shù)十億的對象時,去處理數(shù)十億個KB級別的文件即使文件系統(tǒng)支持也會顯得很笨重。這樣設(shè)計中的一些假設(shè)和參數(shù),比如IO操作和塊大小就必須重新定義。</P>
<P style="TEXT-INDENT: 2em">三,大部分的文件更新模式是通過在尾部追加數(shù)據(jù)而不是覆蓋現(xiàn)有數(shù)據(jù)。文件內(nèi)部的隨機寫操作幾乎是不存在的。一旦寫完,文件就是只讀的,而且通常是順序讀。大量的數(shù)據(jù)都具有這樣的特點。有些可能是被數(shù)據(jù)分析程序掃描的庫組成,有些可能是由運行中的應(yīng)用程序持續(xù)生成的數(shù)據(jù)流,有些可能是檔案數(shù)據(jù),有些可能是數(shù)據(jù)需要由一臺機器產(chǎn)生,然后由另一臺機器處理而產(chǎn)生的中間結(jié)果。假設(shè)在大文件上數(shù)據(jù)訪問具有這樣的模式,那么當(dāng)當(dāng)緩存數(shù)據(jù)在客戶端失效后,append操作就成為性能優(yōu)化和原子性的關(guān)鍵。</P>
<P style="TEXT-INDENT: 2em"> 四,應(yīng)用程序和文件系統(tǒng)api的協(xié)同設(shè)計,增加了整個系統(tǒng)的靈活性。比如我們通過放松了GFS的一致性模型大大簡化了文件系統(tǒng),同時也沒有給應(yīng)用程序帶來繁重的負擔(dān)。我們也提供了一個原子性的append操作,這樣多個客戶端就可以對同一個文件并行的進行append操作而不需要彼此間進行額外的同步操作。這些都會在后面進行詳細的討論。</P>
<P style="TEXT-INDENT: 2em"> 目前已經(jīng)有多個GFS集群為了不同的目的而部署起來。最大的那個具有1000個存儲節(jié)點,超過300T的磁盤空間,被來自不同機器的數(shù)百個客戶端持續(xù)訪問著。</P>
<P style="TEXT-INDENT: 2em"> 2 設(shè)計概覽</P>
<P style="TEXT-INDENT: 2em">2.1 假設(shè)</P>
<P style="TEXT-INDENT: 2em">在設(shè)計一個滿足我們需求的文件系統(tǒng)時,我們以一些充滿了挑戰(zhàn)和機遇的假設(shè)作為指南,之前我們曾間接的提到過一些關(guān)鍵的點,現(xiàn)在我們把這些假設(shè)再詳細的列出來。</P>
<P style="TEXT-INDENT: 2em"> 系統(tǒng)是由廉價的經(jīng)常失敗的商品化組件構(gòu)建而來。必須進行經(jīng)常性的監(jiān)控和檢測,容錯,并且能夠從組件失敗中迅速的恢復(fù),這些都應(yīng)該像是例行公事。</P>
<P style="TEXT-INDENT: 2em"> 系統(tǒng)存儲了適度個數(shù)的大文件。我們期望有數(shù)百萬個文件,每個100mb或者更大。上GB的文件大小應(yīng)該是很普通的情況而且能被有效的管理。小文件也應(yīng)該被支持,但我們不需要為它們進行優(yōu)化。</P>
<P style="TEXT-INDENT: 2em"> 工作負載主要由兩種類型的讀組成:大的順序流式讀取和小的隨機讀取。在大的流式讀取中,單個操作通常要讀取數(shù)百k,甚至1m或者更大的數(shù)據(jù)。來自于同一個客戶端的連續(xù)讀取,通常讀完文件的一個連續(xù)區(qū)域。小的隨機讀取通常在某個任意的偏移位置讀取幾kb的數(shù)據(jù)。具有性能意識的應(yīng)用程序會把這些小的隨機讀取按批次,排序使得讀取可以穩(wěn)步的穿越整個文件而不是來回讀取。</P>
<P style="TEXT-INDENT: 2em"> 工作負載有很多大的對文件數(shù)據(jù)的append操作,通常操作的大小類似與讀操作。一旦寫完,件就很少改變,在文件內(nèi)部的隨機寫操作可以被支持,但是性能不必是很高的。</P>
<P style="TEXT-INDENT: 2em"> 系統(tǒng)對于良好定義的多個客戶端對相同文件的并行append操作必須提供有效的實現(xiàn)。我們的文件通常是用來作為生產(chǎn)者消費者隊列或者進行多路歸并。數(shù)百個生產(chǎn)者(每個機器上運行一個)將會對同一個文件進行append。因此具有最小化同步開銷的原子性是必要的。文件之后可能會被讀取,或者消費者可能并行的讀取這個文件。</P>
<P style="TEXT-INDENT: 2em"> 高的持續(xù)帶寬比低延時更重要。我們大部分的目標應(yīng)用都希望得到高速的批量數(shù)據(jù)處理速度,很少有對于單個的讀寫有嚴格的響應(yīng)時間需求。</P>
<P style="TEXT-INDENT: 2em"> 2.2接口</P>
<P style="TEXT-INDENT: 2em">GFS提供一個熟悉的文件系統(tǒng)接口,盡管它并沒有實現(xiàn)一個諸如POSIX那樣的標準API。文件通過目錄進行層次化組織,通過路徑來標識文件。支持常見的那些文件操作:create,delete,open,close,read,write。</P>
<P style="TEXT-INDENT: 2em"> 另外,GFS還具有快照和append操作?煺找院艿偷拈_銷創(chuàng)建一個文件或者目錄數(shù)的拷貝。Append操作允許多個客戶端向同一個文件并發(fā)的操作,同時保證每個獨立客戶端的append操作的原子性。這對于實現(xiàn)多路歸并的結(jié)果以及生成者消費者隊列很有幫助,這樣客戶端不需要額外的鎖操作就可以并行的append。我們發(fā)現(xiàn)這種文件類型,對于構(gòu)建大型的分布式應(yīng)用簡直就是無價之寶?煺蘸蚢ppend操作將會在3.4和3.3分別討論。</P>
<P style="TEXT-INDENT: 2em"> 2.3架構(gòu)</P>
<P style="TEXT-INDENT: 2em">一個GFS集群由一個master和多個chunkserver組成,可以被多個client訪問,如圖1所示。</P>
<P style="TEXT-INDENT: 2em"></P>
<P style="TEXT-INDENT: 2em">它們都是一個運行著用戶級服務(wù)進程的商品化linux機器?梢院苋菀椎脑谕慌_機器上運行一個chunkserver和client,只要機器資源允許以及由于運行可能的片狀應(yīng)用程序代碼帶來的低可靠性是可以接受的。</P>
<P style="TEXT-INDENT: 2em"> 文件被劃分成固定大小的chunk。每個chunk是由chunk創(chuàng)建時由master分配的一個不可變的全局唯一的64bit句柄來標識。Chunkserver將chunk作為linux文件存儲在本地,對于chunk數(shù)據(jù)的讀寫通過chunk的handle和字節(jié)邊界來表示。為了可靠性,每個chunk存儲在多個chunkserver上。盡管用戶可以為不同文件名字空間區(qū)域指定不同的備份級別,默認地我們存儲三個備份。</P>
<P style="TEXT-INDENT: 2em"> Master維護所有的文件系統(tǒng)元數(shù)據(jù)。包括名字空間,訪問控制信息,文件與chunk的映射信息,chunk的當(dāng)前位置。它也控制系統(tǒng)范圍內(nèi)的一些活動,比如chunk租賃管理,僵死chunk的垃圾回收,chunkserver間的chunk遷移。Master與chunkserver通過心跳信息進行周期性的通信,以發(fā)送指令和收集chunkserver的狀態(tài)。</P>
<P style="TEXT-INDENT: 2em">應(yīng)用程序鏈接的GFS客戶端代碼實現(xiàn)了文件系統(tǒng)API以及代表應(yīng)用程序與master和chunkserver進行通信以讀寫數(shù)據(jù)?蛻舳巳绻枰僮髟獢(shù)據(jù)則需要與master通信,但是所有的純數(shù)據(jù)通信直接與chunksever通信。我們沒有提供POSIX API,因此也就不需要與linux vnode layer關(guān)聯(lián)。</P>
<P style="TEXT-INDENT: 2em"> 客戶端或者chunkserver都不會進行文件數(shù)據(jù)緩存?蛻舳司彺嬷荒艿玫胶苌俚暮锰,因為大部分的應(yīng)用需要直接讀取整個大文件或者工作集合太大根本無法緩存。沒有cache簡化了客戶端和整個系統(tǒng),因為不需要考慮緩存一致性問題(實際上客戶端會緩存元數(shù)據(jù))。Chunkserver不需要進行文件數(shù)據(jù)緩存,是因為chunk是作為本地文件存儲,這樣linux自身會將那些經(jīng)常訪問的數(shù)據(jù)進行緩存。</P>
<P style="TEXT-INDENT: 2em"> 2.4 單Master</P>
<P style="TEXT-INDENT: 2em"> 只有一個master大大簡化了我們的設(shè)計,而且使得master可以利用全局信息對chunk的放置和備份進行更好的判斷。然而,我們必須最小化它在讀寫中的參與性,使得它不會成為一個瓶頸。Client永遠不會通過master讀取文件數(shù)據(jù),它只是問master它應(yīng)該同哪個chunkserver聯(lián)系。并且client將這些信息在有限的時間段內(nèi)進行緩存,直接與chunksever交互進行很多后續(xù)的操作。</P>
<P align=center><A href="http://blog.chinaunix.nethttp://blog.chinaunix.net/attachment/201101/20/25098298_1295515171uP3t.png" target=_blank><IMG height=253 src="http://blog.chinaunix.nethttp://blog.chinaunix.net/attachment/201101/20/25098298_1295515171uP3t.png" width=588 border=0 ; .load="imgResize(this, 650);"></A></P>
<P style="TEXT-INDENT: 2em"> 根據(jù)圖1,我們簡單解釋一些一個讀操作的交互過程:首先,通過固定大小的chunk,客戶端將應(yīng)用程序中標識的文件名和offset轉(zhuǎn)換為chunk的index。然后給master發(fā)送一個包含文件名和chunk index的請求,master返回相應(yīng)的chunk的handle和所有備份的位置?蛻舳艘晕募蚦hunk index為key將這條信息進行緩存。</P>
<P style="TEXT-INDENT: 2em"> 然后客戶端給其中一個備份發(fā)送一個請求,通常是最近的那個。請求標識了chunk 的handle以及在那個chunk內(nèi)的字節(jié)邊界。直到緩存信息過期或者重新打開文件之前,對于相同chunk的后續(xù)讀操作就不需要client-master的通信了。事實上,客戶端通常在一個請求中查詢多個chunk的信息,master也可以將這些被請求的多個chunk的信息包裹在一塊進行返回。這種特別的信息,并沒有額外的花費就避免了未來的client-master的多次通信。</P>
<P style="TEXT-INDENT: 2em"> 2.5 chunk大小</P>
<P style="TEXT-INDENT: 2em">Chunk大小是一個關(guān)鍵的設(shè)計參數(shù)。我們選擇了64mb,遠遠大于現(xiàn)有的文件系統(tǒng)塊。每個chunk的副本作為普通的linux文件存儲在chunkserver上,如果需要才會進行擴展。Lazy空間分配避免了內(nèi)部碎片造成的空間浪費,很可能最大的碎片有向一個chunk那么大。</P>
<P style="TEXT-INDENT: 2em"> 大的chunk size提供了幾個重要的優(yōu)勢。首先,降低了client與sever的交互需求,因為在相同chunk上的讀寫只需要一個初始化請求就可以從master得到chunk的位置信息。這個減少對于我們的應(yīng)用負載是非常明顯的,因為我們應(yīng)用大部分需要順序的讀寫整個大文件。即使對于小的隨機讀取,客戶端也可以很容易的緩存一個幾TB工作集的所有chunk的位置信息。其次,由于chunk很大,那么客戶端就很有可能在一個給定的chunk上執(zhí)行更多的操作,這樣可以將一個與chunkserver的TCP連接保持更長的時間,這就減少了網(wǎng)絡(luò)開銷。再者,降低了存儲在master上的元數(shù)據(jù)大小。這樣就允許我們將元數(shù)據(jù)存放在內(nèi)存中,反過來就帶來了我們將在2.6.1中討論的其他優(yōu)勢。</P>
<P style="TEXT-INDENT: 2em"> 另一方面,大的chunk size,即使采用了lazy空間分配,也有它的缺點。小的文件可能只有少數(shù)幾個chunk,或許只有一個。如果很多的client都需要訪問這個文件,這樣那些存儲了這些chunk的chunkserver就會變成熱點。實際中,熱點還沒有成為一個主要的考慮點因為我們的應(yīng)用絕大部分都是在順序讀大的多chunk文件。</P>
<P style="TEXT-INDENT: 2em"> 然而,當(dāng)GFS第一次使用在一個批處理隊列系統(tǒng)時,熱點確實出現(xiàn)了:一個可執(zhí)行文件作為一個chunk的文件寫到GFS,然后同時在數(shù)百臺機器上開始執(zhí)行。存儲了該可執(zhí)行文件的那些chunkserver被數(shù)百個并發(fā)請求瞬間變成超載。我們通過更高的備份級別存儲這樣的可執(zhí)行文件以及減慢隊列系統(tǒng)的應(yīng)用程序啟動時間解決了這個問題。一個潛在的長遠的解決方案是在這種情況下,允許客戶端從其他客戶端讀取數(shù)據(jù)。</P>
<P style="TEXT-INDENT: 2em"> 2.6元數(shù)據(jù)</P>
<P style="TEXT-INDENT: 2em">Master存儲了三個主要類型的元數(shù)據(jù):文件和chunk名字空間,文件到chunk的映射信息,每個chunk的備份的位置。所有的元數(shù)據(jù)都保存在master的內(nèi)存中。前兩種類型還通過將更新操作的日志保存在本地硬盤和備份在遠程機器來保持持久化。使用log允許我們簡單可靠地更新master的狀態(tài),不用擔(dān)心當(dāng)master crash的時候的不一致性。Master并沒有永久保存chunk的位置信息,而是在master啟動或者某個chunkserver加入集群時,它會向每個chunkserver詢問它的chunks信息。</P>
<P style="TEXT-INDENT: 2em"> 2.6.1 內(nèi)存數(shù)據(jù)結(jié)構(gòu)</P>
<P style="TEXT-INDENT: 2em">由于元數(shù)據(jù)存儲在內(nèi)存里,master的操作是很快的。因此對于master來說,可以簡單有效地對在后臺整個狀態(tài)進行周期性掃描。這個周期性的掃描是用來實現(xiàn)chunk垃圾回收,chunkserver出現(xiàn)失敗時進行的重復(fù)制,以及為了平衡負載和磁盤空間在chunkserver間的chunk 遷移。4.3 4.4將進一步討論這些活動。</P>
<P style="TEXT-INDENT: 2em"> 這樣全內(nèi)存策略存在一個潛在的限制就是chunk的數(shù)目,因此整個系統(tǒng)的容量取決于master有多少可用內(nèi)存。實際中這不是一個很嚴重的限制。Master為每個64MB的chunk維護少于64byte的數(shù)據(jù)。大部分的chunk是滿的,因為大部分的文件包含多個chunk,只有最后一個chunk可能是未慢的。類似的,每個文件名字空間數(shù)據(jù)通常需要少于64byte因為文件名稱存儲時會使用前綴壓縮算法進行壓縮。</P>
<P style="TEXT-INDENT: 2em"> 如果需要支持更大的文件系統(tǒng),只需要往master里添加內(nèi)存。這點開銷與通過將元數(shù)據(jù)存儲到內(nèi)存所得到簡單性,可靠性,性能和靈活性,將是很小的一筆花費。</P>
<P style="TEXT-INDENT: 2em"> 2.6.2 chunk location</P>
<P style="TEXT-INDENT: 2em"> Master并沒有提供一個永久性的存儲保存對于一個給定的chunk都是那些chunkserver保存了它的副本。它只是在啟動時,簡單地從chunkserver那里把這些信息拉過來。Master能夠保證它自己是更新過的,因為是由它來控制chunk的放置,以及通過周期性的心跳信息來監(jiān)控chunkserver。</P>
<P style="TEXT-INDENT: 2em"> 起初,我們嘗試將chunk位置信息永久保存在master,但是我們發(fā)現(xiàn)在啟動時去chunkserver請求這些數(shù)據(jù)更簡單。這樣避免了當(dāng)chunkserver在加入或者離開集群,改名,失敗,重啟等待時需要的master與chunkserver間的同步。在一個數(shù)百臺機器的集群中,這樣的事件太經(jīng)常了。</P>
<P style="TEXT-INDENT: 2em"> 理解這個設(shè)計決定的另一個方式是chunkserver對于自己有還是沒有某個chunk具有最終的發(fā)言權(quán)。在master上維護一個這些信息一致性視圖是沒有意義的,因為發(fā)生在chunkserver上的錯誤可能使得一些chunk突然間不見了(比如硬盤可能會壞掉或者不可用),一個操作可能將chunkserver重命名。</P>
<P style="TEXT-INDENT: 2em"> 2.6.3操作日志</P>
<P style="TEXT-INDENT: 2em">操作日志包含了關(guān)鍵元數(shù)據(jù)改變的歷史記錄。它是GFS的核心。它不僅是元數(shù)據(jù)的唯一一致性記錄,而且它也定義了那些并發(fā)操作的邏輯上的時間表。文件和chunk的版本都是唯一和永恒地由它們創(chuàng)建時的邏輯時間來標識的。</P>
<P style="TEXT-INDENT: 2em"> 因此操作日志是很關(guān)鍵的,我們必須可靠地保存它,在任何元數(shù)據(jù)變更被持久化之前不應(yīng)當(dāng)被客戶端看到。否則,我們將丟失整個文件系統(tǒng)或者最近的客戶端操作即使chunckserver自己保存了它們。因此我們將它備份在多個遠程機器上,對于一個客戶端操作只有當(dāng)該操作對應(yīng)的日志記錄被刷新到本地和遠程的磁盤上時才會發(fā)出響應(yīng)。Master將幾個操作日志捆在一塊刷新,從而降低刷新和復(fù)制對于整個系統(tǒng)吞吐率的影響。</P>
<P style="TEXT-INDENT: 2em"> Master通過重新執(zhí)行操作日志來恢復(fù)它的文件系統(tǒng)。為了最小化啟動時間,我們必須將日志是保持在很小的規(guī)模。當(dāng)日志增長超過一定的大小后,Master給它的狀態(tài)設(shè)置檢查點,它可以通過從本地磁盤加載最新的檢查點進行恢復(fù),然后重新執(zhí)行那些在該檢查點之后的日志記錄。檢查點保存了一個壓縮的類B樹的結(jié)構(gòu),不需要額外的解析就可以直接映射到內(nèi)存用于名字空間查找。這大大提高了恢復(fù)的速度和可用性。</P>
<P style="TEXT-INDENT: 2em"> 因為建立一個檢查點會花費一些時間,master的內(nèi)部狀態(tài)的結(jié)構(gòu)設(shè)計使得一個新的檢查點可以不需要延時那些接受到的變化就可以被創(chuàng)建。Master會啟動一個新的線程切換到一個新的日志文件然后創(chuàng)建新的檢查點。這個新的檢查點包含在切換之前的所有變更。對于一個包含幾百萬文件的集群大概需要幾分鐘就可以完成。結(jié)束后,它將會被寫回本地和遠程的磁盤。</P>
<P style="TEXT-INDENT: 2em"> 恢復(fù)只需要最新完全的檢查點和后來的日志文件。更老的檢查點和日志文件可以自由的刪除,當(dāng)然我們會保存了一些來應(yīng)對某些突發(fā)情況。在創(chuàng)建檢查點的時候發(fā)生的失敗不會影響系統(tǒng)的正確性,因為恢復(fù)代碼會檢測和跳過不完全的檢查點。</P>
<P style="TEXT-INDENT: 2em"> 2.7一致性模型</P>
<P style="TEXT-INDENT: 2em"> GFS使用的一個放松的一致性模型不但很好的支持了我們的高度分布式的應(yīng)用,而且實現(xiàn)起來也相對簡單和有效率。我們現(xiàn)在討論GFS所提供的保證以及它們對應(yīng)用程序的意味著什么。我們也會講述GFS如何維護這些保證,但是會將具體的細節(jié)留到其他論文里講述。</P>
<P style="TEXT-INDENT: 2em"> 2.7.1 GFS提供的保證</P>
<P style="TEXT-INDENT: 2em">文件名字空間的改變(比如文件創(chuàng)建)是原子性的。它們只由master進行處理:名字空間鎖來保證原子性和正確性(4.1節(jié))。Master的操作日志定義了這些操作的全局性的排序。</P>
<P style="TEXT-INDENT: 2em"> 當(dāng)數(shù)據(jù)變更后,文件區(qū)域的狀態(tài)取決于變更的類型,變更是否成功以及是否是并發(fā)進行的。表1是對結(jié)果的一個概述。</P>
<P style="TEXT-INDENT: 2em"><A href="http://blog.chinaunix.nethttp://blog.chinaunix.net/attachment/201101/20/25098298_1295515229WIDR.png" target=_blank><IMG src="http://blog.chinaunix.nethttp://blog.chinaunix.net/attachment/201101/20/25098298_1295515229WIDR.png" border=0 ; .load="imgResize(this, 650);"></A></P>
<P style="TEXT-INDENT: 2em"></P>
<P style="TEXT-INDENT: 2em">如果所有的客戶端無論從哪個副本讀取數(shù)據(jù)總是看到相同的數(shù)據(jù),那么我們就說文件區(qū)域是一致的。如果文件數(shù)據(jù)變更后是一致的,同時客戶端可以看到它所有的變更,那么我們就說文件區(qū)是已定義的。當(dāng)一個變更成功后,且沒有受到其他并發(fā)寫者的影響,那么被影響的區(qū)域就是定義良好的(肯定是一致性的):所有的客戶端將會看到所做的變更。并發(fā)的成功的變更,會使區(qū)域進入未定義的狀態(tài)但是還是一致的:所有的客戶端可以看到一致的數(shù)據(jù),但是它可能無法看到所有的變更(如果變更是針對相同的數(shù)據(jù)寫這樣有的變更就會被新的變更所覆蓋,這樣用戶就無法看到最先的變更了,同時發(fā)生在跨chunk的操作會被拆分成兩個操作,這樣這個操作的一部分可能會被其他操作覆蓋,而另一部分則保留下來,如3.1節(jié)末尾所述)。通常它看到的是多個變更組合后的結(jié)果。一個失敗的變更會使區(qū)域進入非一致的狀態(tài)(因此也是未定義的狀態(tài)):不同的客戶端在不同的訪問中可能看到不同的數(shù)據(jù)。我們下面描述下我們的應(yīng)用程序如何區(qū)分定義良好的區(qū)域和未定義的區(qū)域。應(yīng)用程序不需要進一步區(qū)分未定義區(qū)域的各種不同的類型。</P>
<P style="TEXT-INDENT: 2em"> 數(shù)據(jù)變更可能是寫或者記錄append。寫操作會使數(shù)據(jù)在應(yīng)用程序指定的偏移位置寫入。記錄append操作會使數(shù)據(jù)原子性的append,如果是并發(fā)性的話則至少會被append一次,但是偏移位置是由GFS決定的(然而,通常的理解可能是在客戶端想寫入的那個文件的尾部)。偏移位置會被返回給客戶端,同時標記包含這條記錄的那個定義良好的文件區(qū)域的起始位置。另外GFS可能會在它們之間插入一些padding或者記錄的副本。它們會占據(jù)那些被認為是不一致的區(qū)域,通常它們比用戶數(shù)據(jù)小的多。</P>
<P style="TEXT-INDENT: 2em"> 在一系列成功的變更之后,變更的文件區(qū)域被保證是已定義的,同時包含了最后一次變更的數(shù)據(jù)寫入。GFS通過兩種方式來實現(xiàn)這種結(jié)果a.將這些變更以相同的操作順序應(yīng)用在該chunk的所有的副本上,b.使用chunk的版本號來檢測那些老舊的副本可能是由于它的chunkserver掛掉了而丟失了一些變更。陳舊的副本永遠都不會參與變更或者返回給那些向master詢問chunk位置的client。它們會優(yōu)先參與垃圾回收。</P>
<P style="TEXT-INDENT: 2em"> 因為客戶端會緩存chunk的位置,在信息更新之前它們可能會讀到陳舊的副本。時間窗口由緩存值的超時時間以及文件的下一次打開而限制,文件的打開會清楚緩存中該文件相關(guān)的chunk信息。此外,由于我們的大部分操作都是記錄的append,因此一個陳舊副本通常會返回一個過早結(jié)束的chunk而不是過時的數(shù)據(jù)。當(dāng)讀取者重試并與master聯(lián)系時,它會立即得到當(dāng)前的chunk位置。</P>
<P style="TEXT-INDENT: 2em"> 成功的變更很久之后,組件失敗仍有可能破壞或者污染數(shù)據(jù)。GFS通過周期性的在master和所有chunkserver間握手找到那些失敗的chunkserver,同時通過校驗和(5.2節(jié))來檢測數(shù)據(jù)的污染。一旦發(fā)現(xiàn)問題,會盡快的利用正確的副本恢復(fù)(4.3節(jié))。只有一個塊的所有副本在GFS做出反應(yīng)之前,全部丟失,這個塊才會不可逆轉(zhuǎn)的丟失,而通常GFS的反應(yīng)是在幾分鐘內(nèi)的。即使在這種情況下,塊不可用,而不是被污染:應(yīng)用程序會收到清晰的錯誤信息而不是被污染的數(shù)據(jù)。</P>
<P style="TEXT-INDENT: 2em">2.7.2 對于應(yīng)用程序的影響</P>
<P style="TEXT-INDENT: 2em">GFS應(yīng)用程序可以通過使用簡單的技術(shù)來適應(yīng)這種放松的一致性模型,這些技術(shù)已經(jīng)為其他目的所需要:依賴與append操作而不是覆蓋,檢查點,寫時自我驗證,自己標識記錄。</P>
<P style="TEXT-INDENT: 2em"> 實際中,我們所有的應(yīng)用程序都是通過append而不是覆蓋來改變文件。在一個典型應(yīng)用中,一個寫操作者會從頭至尾生成一個文件。當(dāng)寫完所有數(shù)據(jù)后它自動的將文件重命名為一個永久性的名稱,或者通過周期性的檢查點檢查已經(jīng)有多少數(shù)據(jù)被成功寫入了。檢查點可能會設(shè)置應(yīng)用級的校驗和。讀取者僅驗證和處理最后一個檢查點之前的文件區(qū)域,這些區(qū)域處于已定義的狀態(tài)。無論什么樣的并發(fā)和一致性要求,這個方法都工作的很好。Append操作比隨機寫對于應(yīng)用程序的失敗處理起來總是要更加有效和富有彈性。檢查點允許寫操作者增量性的重啟(不需要重新從頭寫),允許讀取者可以處理那些已經(jīng)成功寫入的數(shù)據(jù),雖然在應(yīng)該程序的看來仍然是不完全的。</P>
<P style="TEXT-INDENT: 2em"> 另一種典型的應(yīng)用中,很多寫者同時向一個文件append為了歸并文件或者是作為一個生產(chǎn)者消費者隊列。記錄的append的append-at-least-once語義保證了每個寫者的輸出。讀取者這樣處理偶然的padding和重復(fù)數(shù)據(jù)。寫者為每條記錄準備一些額外信息比如校驗和,這樣它的合法性就可以驗證。如果不能容忍重復(fù)的數(shù)據(jù)(比如它們可能觸發(fā)非冪等操作),可以通過在記錄中使用唯一標識符來過濾它們,很多時候都需要這些標識符命名相應(yīng)的應(yīng)用程序?qū)嶓w,比如網(wǎng)頁文檔。這些用于record輸入輸出的功能函數(shù)是以庫的形式被我們的應(yīng)用程序共享的,同時應(yīng)用于gongle其他的文件接口實現(xiàn)。所以,相同系列的記錄,加上一些罕見的重復(fù),總是直接被分發(fā)給記錄讀取者。</P>
<P style="TEXT-INDENT: 2em"> 在以上的描述中,存在一個基本的假定:數(shù)據(jù)是以record形式存儲的,而且通常這些record都是可以重復(fù)的,比如一個網(wǎng)頁文檔我們可以重復(fù)存,這對于數(shù)百億的網(wǎng)頁文檔來說,存儲少數(shù)多余的很正常,也就是說這些數(shù)據(jù)通常是文本,而不是二進制,所以我們才可以在append或者寫時用記錄的副本來覆蓋非一致的區(qū)域,所以提供了append的append-at-least-once語義,因為append二次也是可以的。如果我們要保證唯一性,可以在應(yīng)用層增加邏輯。</P>
<P style="TEXT-INDENT: 2em">3.系統(tǒng)交互</P>
<P style="TEXT-INDENT: 2em">我們是以盡量最小化master在所有操作中的參與度來設(shè)計系統(tǒng)的。在這個背景下,我們現(xiàn)在描述下client,master以及chunkserver如何交互來實現(xiàn)數(shù)據(jù)變更,記錄append以及快照的。 </P>
<P style="TEXT-INDENT: 2em">3.1租約和變更順序</P>
<P style="TEXT-INDENT: 2em">一個變更是指一個改變chunk的內(nèi)容或者元信息的操作,比如寫操作或者append操作。每個變更都需要在所有的副本上執(zhí)行。我們使用租約來保持多個副本間變更順序的一致性。Master授權(quán)給其中的一個副本一個該chunk的租約,我們把它叫做主副本。這個主副本為針對該chunk的所有變更的選擇一個執(zhí)行順序,然后所有的副本根據(jù)這個順序執(zhí)行變更。因此,全局的變更順序首先是由master選擇的租約授權(quán)順序來確定的(可能有多個chunk需要進行修改),而同一個租約內(nèi)的變更順序則是由那個主副本來定義的。 </P>
<P style="TEXT-INDENT: 2em">租約機制是為了最小化master的管理開銷而設(shè)計的。一個租約有一個初始化為60s的超時時間設(shè)置。然而只要這個chunk正在變更,那個主副本就可以向master請求延長租約。這些請求和授權(quán)通常是與master和chunkserver間的心跳信息一起發(fā)送的。有時候master可能想在租約過期前撤銷它(比如,master可能想使對一個正在重命名的文件的變更無效)。即使master無法與主副本進行通信,它也可以在舊的租約過期后安全的將租約授權(quán)給另一個新的副本。 </P>
<P style="TEXT-INDENT: 2em">如圖2,我們將用如下的數(shù)字標識的步驟來表示一個寫操作的控制流程。</P>
<P style="TEXT-INDENT: 2em"><A href="http://blog.chinaunix.nethttp://blog.chinaunix.net/attachment/201101/20/25098298_12955153451MDl.png" target=_blank><IMG style="WIDTH: 413px; HEIGHT: 370px" height=370 src="http://blog.chinaunix.nethttp://blog.chinaunix.net/attachment/201101/20/25098298_12955153451MDl.png" width=420 border=0 ; .load="imgResize(this, 650);"></A></P>
<P style="TEXT-INDENT: 2em">1.client向master詢問那個chunkserver獲取了當(dāng)前chunk的租約以及其他副本所在的位置。如果沒有人得到租約,master將租約授權(quán)給它選擇的一個副本。</P>
<P style="TEXT-INDENT: 2em">2.master返回該主副本的標識符以及其他副本的位置。Client為未來的變更緩存這個數(shù)據(jù)。只有當(dāng)主副本沒有響應(yīng)或者租約到期時它才需要與master聯(lián)系。</P>
<P style="TEXT-INDENT: 2em">3.client將數(shù)據(jù)推送給所有的副本,client可以以任意的順序進行推送。每個chunkserver會將數(shù)據(jù)存放在內(nèi)部的LRU buffer里,直到數(shù)據(jù)被使用或者過期。通過將控制流與數(shù)據(jù)流分離,我們可以通過將昂貴的數(shù)據(jù)流基于網(wǎng)絡(luò)拓撲進行調(diào)度來提高性能,而不用考慮哪個chunkserver是主副本。3.2節(jié)更深入地討論了這點。</P>
<P style="TEXT-INDENT: 2em">4.一旦所有的副本接受到了數(shù)據(jù),client發(fā)送一個寫請求給主副本,這個請求標識了先前推送給所有副本的數(shù)據(jù)。主副本會給它收到的所有變更(可能來自多個client)安排一個連續(xù)的序列號來進行必需的串行化。它將這些變更根據(jù)序列號應(yīng)用在本地副本上。</P>
<P style="TEXT-INDENT: 2em">5.主副本將寫請求發(fā)送給所有的次副本,每個次副本以與主副本相同的串行化順序應(yīng)用這些變更。</P>
<P style="TEXT-INDENT: 2em">6.所有的次副本完成操作后向主副本返回應(yīng)答</P>
<P style="TEXT-INDENT: 2em">7.主副本向client返回應(yīng)答。任何副本碰到的錯誤都會返回給client。出現(xiàn)錯誤時,該寫操作可能已經(jīng)在主副本以及一部分次副本上執(zhí)行成功。(如果主副本失敗,那么它不會安排一個序列號并且發(fā)送給其他人)?蛻舳苏埱髮徽J為是失敗的,被修改的區(qū)域?qū)幵诜且恢聽顟B(tài)下。我們的客戶端代碼會通過重試變更來處理這樣的錯誤。它會首先在3-7步驟間進行一些嘗試后在重新從頭重試這個寫操作。</P>
<P style="TEXT-INDENT: 2em"></P>
<P style="TEXT-INDENT: 2em">如果應(yīng)用程序的一個寫操作很大或者跨越了chunk的邊界,GFS client代碼會將它轉(zhuǎn)化為多個寫操作。它們都會遵循上面的控制流程,但是可能會被來自其他client的操作插入或者覆蓋。因此共享的文件區(qū)域可能會包含來自不同client的片段,雖然這些副本是一致的,因為所有的操作都按照相同的順序在所有副本上執(zhí)行成功了。但是文件區(qū)域會處在一種一致但是未定義的狀態(tài),正如2.7節(jié)描述的那樣。</P>
<P style="TEXT-INDENT: 2em"> 3.2數(shù)據(jù)流</P>
<P style="TEXT-INDENT: 2em">為了更有效的使用網(wǎng)絡(luò)我們將數(shù)據(jù)流和控制流分離?刂屏鲝腸lient到達主副本,然后到達其他的所有次副本,而數(shù)據(jù)則是線性地通過一個仔細選擇的chunkserver鏈像流水線那樣推送過去的。我們的目標是充分利用每個機器的網(wǎng)絡(luò)帶寬,避免網(wǎng)絡(luò)瓶頸和高延時鏈路,最小化數(shù)據(jù)推送的延時。</P>
<P style="TEXT-INDENT: 2em"> 為了充分利用每個機器的網(wǎng)絡(luò)帶寬,數(shù)據(jù)通過chunkserver鏈線性的推送過去而不是以其他的拓撲進行分布比如樹型。因此每個機器的帶寬可以全部用來發(fā)送數(shù)據(jù)而不是為多個接受者進行切分。</P>
<P style="TEXT-INDENT: 2em"> 為了盡可能的避免網(wǎng)絡(luò)瓶頸和高延時鏈路,每個機器向網(wǎng)絡(luò)中還沒有收到該數(shù)據(jù)的最近的那個機器推送數(shù)據(jù)。假設(shè)client將數(shù)據(jù)推送給S1- S4,它會首先將數(shù)據(jù)推送給最近的chunkserver假設(shè)是S1,S1推送給最近的,假設(shè)S2,S2推送給S3,S4中離他最近的那個。我們網(wǎng)絡(luò)拓撲足夠簡單,以至于距離可以通過IP地址估計出來。</P>
<P style="TEXT-INDENT: 2em"> 最后為了最小化延時,我們通過將TCP數(shù)據(jù)傳輸進行流水化。一旦一個chunkserver收到數(shù)據(jù),它就開始立即往下發(fā)送數(shù)據(jù)。流水線對我們來說尤其有用,因為我們使用了一個全雙工鏈路的交換網(wǎng)絡(luò)。立即發(fā)送數(shù)據(jù)并不會降低數(shù)據(jù)接受速率。如果沒有網(wǎng)絡(luò)擁塞,向R個副本傳輸B字節(jié)的數(shù)據(jù)理想的時間耗費是B/T+RL,T代表網(wǎng)絡(luò)吞吐率,L是機器間的網(wǎng)絡(luò)延時。我們的網(wǎng)絡(luò)連接是100Mbps(T),L遠遠低于1ms,因此1MB的數(shù)據(jù)理想情況下需要80ms就可以完成。</P>
<P style="TEXT-INDENT: 2em">3.3原子性的記錄append</P>
<P style="TEXT-INDENT: 2em">GFS提供一個原子性的append操作叫做record append(注意這與傳統(tǒng)的append操作也是不同的)。在傳統(tǒng)的寫操作中,用戶指定數(shù)據(jù)需要寫的便宜位置。對于相同區(qū)域的并行寫操作是不可串行的:該區(qū)域的末尾可能包含來自多個client的數(shù)據(jù)片段。但在一個record append操作中,client唯一需要說明的只有數(shù)據(jù)。GFS會將它至少原子性地append到文件中一次,append的位置是由GFS選定的,同時會將這個位置返回給client。這很類似于unix文件打開模式中的O_APPEND,當(dāng)多個寫者并發(fā)操作時不會產(chǎn)生競爭條件。</P>
<P style="TEXT-INDENT: 2em"> Record append在我們的分布式應(yīng)用中被大量的使用。很多在不同機器的client并發(fā)地向同一個文件append。如果使用傳統(tǒng)的寫操作,client將需要進行復(fù)雜而又昂貴的同步化操作,比如通過一個分布式鎖管理器。在我們的工作負載中,這樣的文件通常作為一個多生產(chǎn)者/單消費者隊列或者用來保存來自多個不同client的歸并結(jié)果。</P>
<P style="TEXT-INDENT: 2em"> Record append是一種類型的變更操作,除了一點在主副本上的額外的邏輯外依然遵循3.1節(jié)的控制流。Client將所有的數(shù)據(jù)推送給所有副本后,它向主副本發(fā)送請求。主副本檢查將該記錄append到該chunk是否會導(dǎo)致該chunk超過它的最大值(64MB)。如果超過了,它就將該chunk填充到最大值,告訴次副本做同樣的工作,然后告訴客戶端該操作應(yīng)該在下一個trunk上重試。(append的Record大小需要控制在最大trunk大小的四分之一以內(nèi),這樣可以保證最壞情況下的碎片可以保持在一個可以接受的水平上 )。如果記錄可以沒有超過最大尺寸,就按照普通情況處理,主副本將數(shù)據(jù)append到它的副本上,告訴次副本將數(shù)據(jù)寫在相同的偏移位置上,最后向client返回成功應(yīng)答。</P>
<P style="TEXT-INDENT: 2em"> 如果record append在任何一個副本上失敗,client就會重試這個操作。這樣,相同chunk的多個副本就可能包含不同的數(shù)據(jù),這些數(shù)據(jù)可能包含了相同記錄的整個或者部分的重復(fù)值。GFS并不保證所有的副本在位級別上的一致性,它只保證數(shù)據(jù)作為一個原子單元最少寫入一次。這個屬性是由如下的簡單觀察推導(dǎo)出來的,當(dāng)操作報告成功時,數(shù)據(jù)肯定被寫入到某個trunk的所有副本的相同偏移位置上。此后,所有的副本至少達到了記錄尾部的大小,因此未來的記錄將會被放置在更高的便宜位置,或者是另一個不同的chunk,即使另一個副本變成了主副本。在我們的一致性保證里,record append操作成功后寫下的數(shù)據(jù)區(qū)域是已定義的(肯定是一致的),然而介于其間的數(shù)據(jù)則是不一致的(因此也是未定義的)。我們的應(yīng)用程序可以處理這樣的不一致區(qū)域,正如我們在2.7.2里討論的那樣。</P>
<P style="TEXT-INDENT: 2em"> 3.4快照</P>
<P style="TEXT-INDENT: 2em">快照操作可以非?焖俚谋4嫖募蛘吣夸洏涞囊粋拷貝,同時可以最小化對于正在執(zhí)行的變更操作的中斷。用戶經(jīng)常用它來創(chuàng)建大數(shù)據(jù)集的分支拷貝,以及拷貝的拷貝……;蛘哂脕韯(chuàng)建檢查點,以實驗將要提交的拷貝或者回滾到更早的狀態(tài)。</P>
<P style="TEXT-INDENT: 2em"> 像AFS,我們使用標準的寫時拷貝技術(shù)來實現(xiàn)快照。當(dāng)master收到一個快照請求時,它首先撤銷將要進行快照的那些文件對應(yīng)的chunk的所有已發(fā)出的租約。這就使得對于這些chunk的后續(xù)寫操作需要與master交互來得到租約持有者。這就首先給master一個機會創(chuàng)建該chunk的新的拷貝。</P>
<P style="TEXT-INDENT: 2em"> 當(dāng)這些租約被撤銷或者過期后,master將這些操作以日志形式寫入磁盤。然后復(fù)制該文件或者目錄樹的元數(shù)據(jù),然后將這些日志記錄應(yīng)用到內(nèi)存中的復(fù)制后的狀態(tài)上,新創(chuàng)建的快照文件與源文件一樣指向相同的chunk。</P>
<P style="TEXT-INDENT: 2em"> 當(dāng)client在快照生效后第一次對一個chunk C進行寫入時,它會發(fā)送請求給master找到當(dāng)前租約擁有者。Master注意到對于chunk C的引用計數(shù)大于1。它延遲回復(fù)客戶端的請求,選擇一個新的chunk handle C`。然后讓每個擁有C的那些chunkserver創(chuàng)建一個新的叫做C`的chunk。通過在相同的chunkserver上根據(jù)原始的chunk創(chuàng)建新chunk,就保證了數(shù)據(jù)拷貝是本地地,而不是通過網(wǎng)絡(luò)(我們的硬盤比100Mbps網(wǎng)絡(luò)快大概三倍)。這樣,對于任何chunk的請求處理都沒有什么不同:master為新才chunk C`的副本中的一個授權(quán)租約,然后返回給client,這樣它就可以正常的寫這個chunk了,client不需要知道該chunk實際上是從一個現(xiàn)有的chunk創(chuàng)建出來的。</P>
<P style="TEXT-INDENT: 2em"> 4.master操作</P>
<P style="TEXT-INDENT: 2em">Master執(zhí)行所有的名字空間操作。此外,它還管理整個系統(tǒng)的chunk備份:決定如何放置,創(chuàng)建新的chunk和相應(yīng)的副本,協(xié)調(diào)整個系統(tǒng)的活動保證chunk都是完整備份的,在chunkserver間進行負載平衡,回收沒有使用的存儲空間。我們現(xiàn)在討論這些主題。</P>
<P style="TEXT-INDENT: 2em"> 4.1名字空間管理和鎖</P>
<P style="TEXT-INDENT: 2em">很多master操作都需要花費很長時間:比如,一個快照操作要撤銷該快照所包含的chunk的所有租約。我們并不想耽誤其他運行中的master操作,因此我們允許多個操作同時是活動的,通過在名字空間區(qū)域使用鎖來保證正確的串行化。</P>
<P style="TEXT-INDENT: 2em"> 不像傳統(tǒng)的文件系統(tǒng),GFS的目錄并沒有一種數(shù)據(jù)結(jié)構(gòu)用來列出該目錄下所有文件,而且也不支持文件或者目錄別名(像unix的硬鏈接或者軟連接那樣)。GFS在邏輯上通過一個路徑全稱到元數(shù)據(jù)映射的查找表來表示它的名字空間。通過采用前綴壓縮,這個表可以有效地在內(nèi)存中表示。名字空間樹中的每個節(jié)點(要么是文件的絕對路徑名稱要么是目錄的)具有一個相關(guān)聯(lián)的讀寫鎖。</P>
<P style="TEXT-INDENT: 2em"> 每個master操作在它運行前,需要獲得一個鎖的集合。比如如果它想操作/d1/d2…/dn/leaf,那么它需要獲得/d1,/d1/d2……/d1/d2…/dn這些目錄的讀鎖,然后才能得到路徑/d1/d2…/dn/leaf的讀鎖或者寫鎖。Leaf可能是個文件或者目錄,這取決于具體的操作。</P>
<P style="TEXT-INDENT: 2em"> 我們現(xiàn)在解釋一下,當(dāng)為/home/user創(chuàng)建快照/save/user時,鎖機制如何防止文件/home/user/foo被創(chuàng)建?煺詹僮餍枰@得在/home /save上的讀鎖,以及/home/user和/save/user上的寫鎖。文件創(chuàng)建需要獲得在/home和/home/user上的讀鎖,以及在/home/user/foo上的寫鎖。這兩個操作將會被正確的串行化,因為它們試圖獲取在/home/user上的相沖突的鎖。文件創(chuàng)建并不需要父目錄的寫鎖,因為實際上這里并沒有”目錄”或者說是類似于inode的數(shù)據(jù)結(jié)構(gòu),需要防止被修改。讀鎖已經(jīng)足夠用來防止父目錄被刪除。</P>
<P style="TEXT-INDENT: 2em">這種鎖模式的一個好處就是它允許對相同目錄的并發(fā)變更操作。比如多個文件的創(chuàng)建可以在相同目錄下并發(fā)創(chuàng)建:每個獲得該目錄的一個讀鎖,以及文件的一個寫鎖。目錄名稱上的讀鎖足夠可以防止目錄被刪除,重命名或者快照。文件名稱上的寫鎖將會保證重復(fù)創(chuàng)建相同名稱的文件的操作只會被執(zhí)行一次。</P>
<P style="TEXT-INDENT: 2em">因為名字空間有很多節(jié)點,所以讀寫鎖對象只有在需要時才會被分配,一旦不再使用用就刪除。為了避免死鎖,鎖是按照一個一致的全序關(guān)系進行獲取的:首先根據(jù)所處的名字空間樹的級別,相同級別的則根據(jù)字典序。</P>
<P style="TEXT-INDENT: 2em"> 4.2 備份放置</P>
<P style="TEXT-INDENT: 2em">GFS在多個層次上都具有高度的分布式。它擁有數(shù)百個散步在多個機柜中的chunkserver。這些chunkserver又可以被來自不同或者相同機柜上的client訪問。處在不同機柜的機器間的通信可能需要穿過一個或者更多的網(wǎng)絡(luò)交換機。此外,進出一個機柜的帶寬可能會小于機柜內(nèi)所有機器的帶寬總和。多級的分布式帶來了數(shù)據(jù)分布式時的擴展性,可靠性和可用性方面的挑戰(zhàn)。</P>
<P style="TEXT-INDENT: 2em"> Chunk的備份放置策略服務(wù)于兩個目的:最大化數(shù)據(jù)可靠性和可用性,最小化網(wǎng)絡(luò)帶寬的使用。為了達到這兩個目的,僅僅將備份放在不同的機器是不夠的,這只能應(yīng)對機器或者硬盤失敗,以及最大化利用每臺機器的帶寬。我們必須在機柜間存放備份。這樣能夠保證當(dāng)一個機柜整個損壞或者離線(比如網(wǎng)絡(luò)交換機故障或者電路出問題)時,該chunk的存放在其他機柜的某些副本仍然是可用的。這也意味著對于一個chunk的流量,尤其是讀取操作可以充分利用多個機柜的帶寬。另一方面,寫操作需要在多個機柜間進行,但這是我們可以接受的。</P>
<P style="TEXT-INDENT: 2em"> 4.3創(chuàng)建 重備份 重平衡</P>
<P style="TEXT-INDENT: 2em">Chunk副本的創(chuàng)建主要有三個原因:chunk的創(chuàng)建,重備份,重平衡。</P>
<P style="TEXT-INDENT: 2em"> 當(dāng)master創(chuàng)建一個chunk時,它將初始化的空的副本放置在何處。它會考慮幾個因素:1.盡量把新的chunk放在那些低于平均磁盤空間使用值的那些chunkserver上。隨著時間的推移,這會使得chunkserver的磁盤使用趨于相同2.盡量限制每個chunkserver上的最近的文件創(chuàng)建數(shù),雖然創(chuàng)建操作是很簡單的,但是它后面往往跟著繁重的寫操作,因為chunk的創(chuàng)建通常是因為寫者的需要而創(chuàng)建它。在我們的一次append多次讀的工作負載類型中,一旦寫入完成,它們就會變成只讀的。3.正如前面討論的,我們希望在機柜間存放chunk的副本</P>
<P style="TEXT-INDENT: 2em"> 當(dāng)chunk的可用備份數(shù)低于用戶設(shè)定的目標值時,Master會進行重復(fù)制。有多個可能的原因?qū)е滤陌l(fā)生:chunkserver不可用,chunkserver報告它的某個備份已被污染,一塊硬盤由于錯誤而不可用或者用戶設(shè)定的目標值變大了。需要重復(fù)制的chunk根據(jù)幾個因素確定優(yōu)先級。一個因素是它與備份數(shù)的目標值差了多少,比如我們給那些丟失了2個副本的chunk比丟失了1個的更高的優(yōu)先級。另外,比起最近被刪除的文件的chunk,我們更想備份那些仍然存在的文件的chunk(參考4.4節(jié))。最后了,為了最小化失敗對于運行中的應(yīng)用程序的影響,我們提高那些阻塞了用戶進度的chunk的優(yōu)先級。</P>
<P style="TEXT-INDENT: 2em"> Master選擇最高優(yōu)先級的chunk,通過給某個chunkserver發(fā)送指令告訴它直接從一個現(xiàn)有合法部分中拷貝數(shù)據(jù)來進行克隆。新備份的放置與創(chuàng)建具有類似的目標:平均磁盤使用,限制在單個chunkserver上進行的clone操作數(shù),使副本存放在不同機柜間。為了防止clone的流量淹沒client的流量,master限制整個集群已經(jīng)每個chunkserver上處在活動狀態(tài)的clone操作數(shù)。另外每個chunkserver還會限制它用在clone操作上的帶寬,通過控制它對源chunkserver的讀請求。</P>
<P style="TEXT-INDENT: 2em">最后,master會周期性的對副本進行重平衡。它檢查當(dāng)前的副本分布,然后為了更好的磁盤空間使用和負載瓶頸,將副本進行移動。而且在這個過程中,master是逐步填充一個新的chunkserver,而不是立即將新的chunk以及大量沉重的寫流量使他忙的不可開交。對于一個新副本的放置,類似于前面的那些討論。另外,master必須選擇刪除哪個現(xiàn)有的副本。通常來說,它更喜歡那些存放在低于平均磁盤空閑率的chunkserver上的chunk,這樣可以使磁盤使用趨于相等。</P>
<P style="TEXT-INDENT: 2em"> 4.4垃圾回收</P>
<P style="TEXT-INDENT: 2em">文件刪除后,GFS并不立即釋放可用的物理存儲。它會將這項工作推遲到文件和chunk級別的垃圾回收時做。我們發(fā)現(xiàn),這種方法使得系統(tǒng)更簡單更可靠。</P>
<P style="TEXT-INDENT: 2em"> 4.4.1機制</P>
<P style="TEXT-INDENT: 2em">當(dāng)文件被應(yīng)用程序刪除時,master會將這個刪除操作像其他變化一樣理解寫入日志。文件不會被立即刪除,而是被重命名為一個包含刪除時間戳的隱藏名稱。在master對文件系統(tǒng)進行常規(guī)掃描時,它會刪除那些存在時間超過3天(這個時間是可以配置的)的隱藏文件。在此之前,文件依然可以用那個新的特殊名稱進行讀取,或者重命名回原來的名稱來取消刪除。當(dāng)隱藏文件從名字空間刪除后,它的元數(shù)據(jù)會被擦除。這樣就有效地切斷了它與所有chunk的關(guān)聯(lián)。</P>
<P style="TEXT-INDENT: 2em"> 在chunk的類似的常規(guī)掃描中,master找到那些孤兒塊(無法從任何文件到達),擦除這些塊的元數(shù)據(jù)。在與master周期**互的心跳信息中,chunkserver報告它所擁有的chunk的那個子集,然后master返回那些不在master的元數(shù)據(jù)中出現(xiàn)的chunk的標識。Chunkserver就可以自由的刪除這些chunk的那些副本了。</P>
<P style="TEXT-INDENT: 2em"> 4.4.2討論</P>
<P style="TEXT-INDENT: 2em">盡管程序設(shè)計語言中的分布式垃圾回收是一個需要復(fù)雜解決方案的難解問題,但是在我們這里它是很簡單的。我們可以簡單的找到對于chunk的所有引用:因為它們保存在只由master維護的一個文件-chunk映射里。我們可以找到所有chunk的副本:它們不過是存放在每個chunkserver的特定目錄下的linux文件。任何master不知道的副本就是垃圾。</P>
<P style="TEXT-INDENT: 2em"> 采用垃圾回收方法收回存儲空間與直接刪除相比,提供了幾個優(yōu)勢:1.在經(jīng)常出現(xiàn)組件失敗的大規(guī)模分布式系統(tǒng)中,它是簡單而且可靠的。Chunk創(chuàng)建可能在某些chunkserver上成功,在另外一些失敗,這樣就留下一些master所不知道的副本。副本刪除消息可能丟失,master必須記得在出現(xiàn)失敗時進行重發(fā)。垃圾回收提供了一種同一的可信賴的清除無用副本的方式。2.它將存儲空間回收與master常規(guī)的后臺活動結(jié)合在一起,比如名字空間掃描,與chunkserver的握手。因此它們是綁在一塊執(zhí)行的,這樣開銷會被平攤。而且只有當(dāng)master相對空閑時才會執(zhí)行。Master就可以為那些具有時間敏感性的客戶端請求提供更好的響應(yīng)。3.空間回收的延遲為意外的不可逆轉(zhuǎn)的刪除提供了一道保護網(wǎng)。</P>
<P style="TEXT-INDENT: 2em"> 根據(jù)我們的經(jīng)驗,主要的缺點是,當(dāng)磁盤空間很緊張時,這種延時會妨礙到用戶對磁盤使用的調(diào)整。那些頻繁創(chuàng)建和刪除中間文件的應(yīng)用程序不能夠立即重用磁盤空間。我們通過當(dāng)已刪除的文件被再次刪除時加速它的存儲回收來解決這個問題。我們也允許用戶在不同的名字空間內(nèi)使用不同的重備份和回收策略。比如用戶可以指定某個目錄樹下的文件的chunk使用無副本存儲,任何已經(jīng)刪除的文件會被立即刪除并且從當(dāng)前文件系統(tǒng)中徹底刪除。</P>
<P style="TEXT-INDENT: 2em"> 4.5過期副本檢測</P>
<P style="TEXT-INDENT: 2em">如果chunkserver失敗或者在它停機期間丟失了某些更新,chunk副本就可能變?yōu)檫^期的。對于每個chunk,master維護一個版本號來區(qū)分最新和過期的副本。</P>
<P style="TEXT-INDENT: 2em"> 只要master為一個chunk授權(quán)一個新的租約,那么它的版本號就會增加,然后通知副本進行更新。在一致的狀態(tài)下,Master和所有副本都會記錄這個新的版本號。這發(fā)生在任何client被通知以前,因此也就是client開始向chunk中寫數(shù)據(jù)之前。如果另一個副本當(dāng)前不可用,它的chunk版本號就不會被更新。當(dāng)chunkserver重啟或者報告它的chunk和對應(yīng)的版本號的時候,master會檢測該chunkserver是否包含過期副本。如果master發(fā)現(xiàn)有些版本號大于它的記錄,master就認為它在授權(quán)租約時失敗了,所以采用更高的版本號的那個進行更新。</P>
<P style="TEXT-INDENT: 2em"> Master通過周期性的垃圾回收刪除過期副本。在此之前,對于客戶端對于該chunk的請求master會直接將過期副本當(dāng)作根本不存在進行處理。作為另外一種保護措施,當(dāng)master通知客戶端那個chunkserver包含某chunk的租約或者當(dāng)它在clone操作中讓chunkserver從另一個chunkserver中讀取chunk時,會將chunk的版本號包含在內(nèi)。當(dāng)clinet和chunkserver執(zhí)行操作時,總是會驗證版本號,這樣就使得它們總是訪問最新的數(shù)據(jù)。</P>
<P style="TEXT-INDENT: 2em"> 5.容錯和診斷</P>
<P style="TEXT-INDENT: 2em">在設(shè)計系統(tǒng)時,一個最大的挑戰(zhàn)就是頻繁的組件失敗。組件的數(shù)量和質(zhì)量使得這些問題變成一種常態(tài)而不再是異常。我們不能完全信任機器也不能完全信任磁盤。組件失敗會導(dǎo)致系統(tǒng)不可用,甚至是損壞數(shù)據(jù)。我們討論下如何面對這些挑戰(zhàn),以及當(dāng)它們不可避免的發(fā)生時,在系統(tǒng)中建立起哪些工具來診斷問題。</P>
<P style="TEXT-INDENT: 2em"> 5.1高可用性</P>
<P style="TEXT-INDENT: 2em">在GFS的數(shù)百臺服務(wù)器中,在任何時間總是有一些是不可用的。我們通過兩個簡單有效的策略來保持整個系統(tǒng)的高可用性:快速恢復(fù)和備份。 </P>
<P style="TEXT-INDENT: 2em">5.1.1快速恢復(fù)</P>
<P style="TEXT-INDENT: 2em">Master和chunkserver都設(shè)計得無論怎么樣地被終止,都可以在在幾秒內(nèi)恢復(fù)它們的狀態(tài)并啟動。事實上,我們并沒有區(qū)分正常和異常的終止。服務(wù)器通常都是通過殺死進程來關(guān)閉?蛻舳撕推渌⻊(wù)器的請求超時后會經(jīng)歷一個小的停頓,然后重連那個重啟后的服務(wù)器,進行重試。6.2.2報告了觀測到的啟動時間。</P>
<P style="TEXT-INDENT: 2em"> 5.1.2chunk備份</P>
<P style="TEXT-INDENT: 2em">正如之前討論的,每個chunk備份在不同機柜上的多個chunkserver上。用戶可以在不同名字空間內(nèi)設(shè)置不同的備份級別,默認是3.當(dāng)chunkserver離線或者通過檢驗和檢測到某個chunk損壞后(5.2節(jié)),master會克隆現(xiàn)有的副本使得副本的數(shù)保持充足。盡管副本已經(jīng)很好的滿足了我們的需求,我們還探尋一些其他的具有同等或者更少code的跨機器的冗余方案,來滿足我們?nèi)找嬖鲩L的只讀存儲需求。我們期望在我們的非常松散耦合的系統(tǒng)中實現(xiàn)這些更復(fù)雜的冗余模式是具有挑戰(zhàn)性但是可管理的。因為我們的負載主要是append和讀操作而不是小的隨機寫操作。</P>
<P style="TEXT-INDENT: 2em"> 5.1.3master備份</P>
<P style="TEXT-INDENT: 2em">為了可靠性,master的狀態(tài)需要進行備份。它的操作日志和檢查點備份在多臺機器上。對于狀態(tài)的變更只有當(dāng)它的操作日志被寫入到本地磁盤和所有的遠程備份后,才認為它完成。為了簡單起見,master除了負責(zé)進行各種后臺活動比如:垃圾回收外,還要負責(zé)處理所有的變更。當(dāng)它失敗后,幾乎可以立即重啟。如果它所在的機器或者硬盤壞了,獨立于GFS的監(jiān)控設(shè)施會利用備份的操作日志在別處重啟一個新的master進程。Client僅僅使用master的一個典型名稱(比如gfs-test)來訪問它,這是一個DNS名稱,如果master被重新部署到一個新的機器上,可以改變它。</P>
<P style="TEXT-INDENT: 2em"> 此外,當(dāng)主master down掉之后,還有多個影子master可以提供對文件系統(tǒng)的只讀訪問。它們是影子,而不是鏡像,這意味著它們可能比主master要滯后一些,通?赡苁菐酌。對于那些很少發(fā)生變更的文件或者不在意輕微過時的應(yīng)用程序來說,它們增強了讀操作的可用性。實際上,因為文件內(nèi)容是從chunkserver中讀取的,應(yīng)用程序并不會看到過期的文件內(nèi)容。文件元數(shù)據(jù)可能在短期內(nèi)是過期的,比如目錄內(nèi)容或者訪問控制信息。</P>
<P style="TEXT-INDENT: 2em"> 為了保持自己的實時性,影子服務(wù)器會讀取不斷增長的操作日志的副本,然后像主master那樣將這些變化序列應(yīng)用在自己的數(shù)據(jù)結(jié)構(gòu)上。與主master一樣,它也會在啟動時向chunkserver拉數(shù)據(jù)來定位chunk的副本,也會同它們交換握手信息以監(jiān)控它們的狀態(tài)。只有在主master決定創(chuàng)建或者刪除副本時引起副本位置信息更新時,它才依賴于主master。</P>
<P style="TEXT-INDENT: 2em"> 5.2數(shù)據(jù)完整性</P>
<P style="TEXT-INDENT: 2em">每個chunkserver通過校驗和來檢測存儲數(shù)據(jù)中的損壞。GFS集群通常具有分布在幾百臺機器上的數(shù)千塊硬盤,這樣它就會經(jīng)常出現(xiàn)導(dǎo)致數(shù)據(jù)損壞或丟失的硬盤失敗。我們可以從chunk的其他副本中恢復(fù)被損壞的數(shù)據(jù),但是如果通過在chunkserver間比較數(shù)據(jù)來檢測數(shù)據(jù)損壞是不現(xiàn)實的。另外,有分歧的備份仍然可能是合法的:根據(jù)GFS的變更語義,尤其是前面提到的原子性的record append操作,并不保證所有副本是完全一致的。因此每個chunkserver必須通過維護一個檢驗和來獨立的驗證它自己的拷貝的完整性。</P>
<P style="TEXT-INDENT: 2em"> 一個chunk被劃分為64kb大小的塊。每個塊有一個相應(yīng)的32bit的校驗和。與其他的元數(shù)據(jù)一樣,校驗和與用戶數(shù)據(jù)分離的,它被存放在內(nèi)存中,同時通過日志進行持久化存儲。</P>
<P style="TEXT-INDENT: 2em"> 對于讀操作,chunkserver在向請求者(可能是一個client或者其他的chunkserver)返回數(shù)據(jù)前,需要檢驗與讀取邊界重疊的那些數(shù)據(jù)庫的校驗和。因此chunkserver不會將損壞數(shù)據(jù)傳播到其他機器上去。如果一個塊的校驗和與記錄中的不一致,chunkserver會向請求者返回一個錯誤,同時向master報告這個不匹配。之后,請求者會向其他副本讀取數(shù)據(jù),而master則會用其他副本來clone這個chunk。當(dāng)這個合法的新副本創(chuàng)建成功后,master向報告不匹配的那個chunkserver發(fā)送指令刪除它的副本。</P>
<P style="TEXT-INDENT: 2em"> 校驗和對于讀性能的影響很小,因為:我們大部分的讀操作至少跨越多個塊,我們只需要讀取相對少的額外數(shù)據(jù)來進行驗證。GFS client代碼通過盡量在校驗邊界上對齊讀操作大大降低了開銷。另外在chunkserver上校驗和的查找和比較不需要任何的IO操作,校驗和的計算也可以與IO操作重疊進行。</P>
<P style="TEXT-INDENT: 2em"> 校驗和計算對于append文件末尾的寫操作進行了特別的優(yōu)化。因為它們在工作負載中占據(jù)了統(tǒng)治地位。我們僅僅增量性的更新最后一個校驗塊的校驗值,同時為那些append尾部的全新的校驗塊計算它的校驗值。即使最后一個部分的校驗塊已經(jīng)損壞,而我們現(xiàn)在無法檢測出它,那么新計算出來的校驗和將不會與存儲數(shù)據(jù)匹配,那么當(dāng)這個塊下次被讀取時,就可以檢測到這個損壞。(也就是說這里并沒有驗證最后一個塊的校驗值,而只是更新它的值,也就是說這里省去了驗證的過程,舉個例子假設(shè)最后一個校驗塊出現(xiàn)了錯誤,由于我們的校驗值計算時是增量性的,也就是說下次計算不會重新計算已存在的這部分數(shù)據(jù)的校驗和,這樣該損壞就繼續(xù)保留在校驗和里,關(guān)鍵是因為這里采用了增量型的校驗和計算方式)</P>
<P style="TEXT-INDENT: 2em"> 與之相對的,如果一個寫操作者覆蓋了一個現(xiàn)有chunk的邊界,我們必須首先讀取和驗證操作邊界上的第一個和最后一個塊,然后執(zhí)行寫操作,最后計算和記錄新的校驗和。如果在覆蓋它們之前不驗證第一個和最后一個塊,新的校驗和就可能隱藏掉那些未被覆蓋的區(qū)域的數(shù)據(jù)損壞。(因為這里沒有采用增量計算方式,因為它是覆蓋不是append所以現(xiàn)有的檢驗和就是整個塊的沒法從中取出部分數(shù)據(jù)的校驗和,必須重新計算)。</P>
<P style="TEXT-INDENT: 2em"> 在空閑期間,chunkserver可以掃描驗證處在非活動狀態(tài)的trunk的內(nèi)容。這允許我們檢測到那些很少被讀取的數(shù)據(jù)的損失。一旦損壞被發(fā)現(xiàn),master就可以創(chuàng)建一個新的未損壞副本并且刪除損壞的副本。這就避免了一個不活躍的壞塊騙過master,讓之以為塊有足夠的好的副本。</P>
<P style="TEXT-INDENT: 2em"> 5.3診斷工具</P>
<P style="TEXT-INDENT: 2em">全面而詳細的診斷性的日志以很小的成本,帶來了在問題分解,調(diào)試,性能分析上不可估量的幫助。沒有日志,就很難理解那些機器間偶然出現(xiàn)的不可重復(fù)的交互。GFS生成一個診斷日志用來記錄很多重要事件(比如chunkserver的啟動停止)以及所有RPC請求和應(yīng)答。這些診斷日志可以自由的刪除而不影響系統(tǒng)的正常運行。然而,只要磁盤空間允許,我們會盡量保存這些日志。</P>
<P style="TEXT-INDENT: 2em"> RPC日志包含了所有的請求和響應(yīng)信息,除了讀寫的文件數(shù)據(jù)。通過匹配請求和響應(yīng),整理不同機器上的RPC日志,我們可以重新構(gòu)建出整個交互歷史來診斷一個問題。這些日志也可以用來進行負載測試和性能分析。</P>
<P style="TEXT-INDENT: 2em"> 因為日志是順序異步寫的,因此寫日志對于性能的影響是很小的,得到的好處卻是大大的。最近的事件也會保存在內(nèi)存中,可以用于持續(xù)的在線監(jiān)控。</P>
<P style="TEXT-INDENT: 2em">6.測量</P>
<P style="TEXT-INDENT: 2em">在這一節(jié),我們用一些小規(guī)模的測試來展示GFS架構(gòu)和實現(xiàn)固有的一些瓶頸,有一些數(shù)字來源于google的實際集群。</P>
<P style="TEXT-INDENT: 2em"> 6.1小規(guī)模測試</P>
<P style="TEXT-INDENT: 2em">我們在一個由一個master,兩個master備份,16個chunkserver,16個client組成的GFS集群上進行了性能測量。這個配置是為了方便測試,實際中的集群通常會有數(shù)百個chunkserver,數(shù)百個client。</P>
<P style="TEXT-INDENT: 2em"> 所有機器的配置是,雙核PIII 1.4GHz處理器,2GB內(nèi)存,兩個80G,5400rpm硬盤,以及100Mbps全雙工以太網(wǎng)連接到HP2524交換機。所有19個GFS服務(wù)器連接在一個交換機,所有16個客戶端連接在另一個上。兩個交換機用1Gbps的線路連接。</P>
<P style="TEXT-INDENT: 2em"> 6.1.1讀操作</P>
<P style="TEXT-INDENT: 2em">N個客戶端從文件系統(tǒng)中并發(fā)讀。每個客戶端在一個320GB的文件集合里隨機4MB進行讀取。然后重復(fù)256次,這樣每個客戶端實際上讀取了1GB數(shù)據(jù)。Chunkserver總共只有32GB內(nèi)存,因此我們估計在linux的buffer cache里最多有10%的命中率。我們的結(jié)果應(yīng)該很接近一個幾乎無緩存的結(jié)果。 </P>
<P style="TEXT-INDENT: 2em"><A href="http://blog.chinaunix.nethttp://blog.chinaunix.net/attachment/201101/20/25098298_1295515601aT8M.png" target=_blank><IMG style="WIDTH: 583px; HEIGHT: 236px" height=236 src="http://blog.chinaunix.nethttp://blog.chinaunix.net/attachment/201101/20/25098298_1295515601aT8M.png" width=574 border=0 ; .load="imgResize(this, 650);"></A></P>
<P style="TEXT-INDENT: 2em">圖3(a)展示了對于N個客戶端的總的讀取速率以及它的理論上的極限。當(dāng)2個交換機通過一個1Gbps的鏈路連接時,它的極限峰值是125MB/s,客戶端通過100Mbps連接,那么換成單個客戶端的極限就是12.5MB/s。當(dāng)只有一個客戶端在讀取時,觀察到的讀取速率是10MB/s,達到了單個客戶端極限的80%。當(dāng)16個讀取者時,總的讀取速率的94 MB/s,大概達到了鏈路極限(125MB/s)的75%,換成單個客戶端就是6 MB/s。效率從80%降到了75%,是因為伴隨著讀取者的增加,多個讀者從同一個chunkserver并發(fā)讀數(shù)據(jù)的概率也隨之變大。</P>
<P style="TEXT-INDENT: 2em"></P>
<P style="TEXT-INDENT: 2em">6.1.2寫操作</P>
<P style="TEXT-INDENT: 2em">N個客戶端并行向N個不同的文件寫數(shù)據(jù)。每個客戶端以1MB的單個寫操作總共向一個新文件寫入1GB數(shù)據(jù)?偟膶懰俾室约八睦碚撋系臉O限如圖3(b)所示。極限值變成了67 MB/s,是因為我們需要將每個字節(jié)寫入到16個chunkserver中的3個,每個具有12.5MB/s的輸入連接。</P>
<P style="TEXT-INDENT: 2em"> 單個客戶端的寫入速率是6.3 MB/s,大概是極限值的一半。主要原因是我們的網(wǎng)絡(luò)協(xié)議棧。它不能充分利用我們用于chunk副本數(shù)據(jù)推送的流水線模式。將數(shù)據(jù)從一個副本傳遞到另一個副本的延遲降低了整體的寫速率。</P>
<P style="TEXT-INDENT: 2em"> 對于16個客戶端,總體的寫入速率達到了35 MB/s,平均每個客戶端2.2 MB/s,大概是理論極限的一半。與寫操作類似,伴隨著寫者的增加,多個寫者從同一個chunkserver并發(fā)寫數(shù)據(jù)的概率也隨之變大。另外對于16個寫者比16個讀者更容易產(chǎn)生碰撞,因為每個寫者將關(guān)聯(lián)到3個不同的副本。</P>
<P style="TEXT-INDENT: 2em"> 寫者比我們期望的要慢。在實際中,這還末變成一個主要問題,因為盡管它可能增加單個客戶端的延時,但是當(dāng)系統(tǒng)面對大量客戶端時,其總的寫入帶寬并沒有顯著的影響。</P>
<P style="TEXT-INDENT: 2em"> 6.1.3記錄追加</P>
<P style="TEXT-INDENT: 2em">圖3(c)展示了record append的性能。N個客戶端向單個文件并行的append。性能取決于保存了該文件最后那個chunk的那些chunkserver,與客戶端的數(shù)目無關(guān)。當(dāng)只有一個客戶端時,能達到6.0MB/s,當(dāng)有16個客戶端時就降到了4.8 MB/s。主要是由于擁塞以及不同的客戶端的網(wǎng)絡(luò)傳輸速率不同造成的。</P>
<P style="TEXT-INDENT: 2em"> 我們的應(yīng)用程序傾向于并行創(chuàng)建多個這樣的文件。換句話說,N個客戶端向M個共享文件并行append,在這里N和M通常是幾十甚至幾百大小。因此在我們的實驗中出現(xiàn)的chunkserver的網(wǎng)絡(luò)擁塞問題在實際中并不是一個顯著的問題,因為當(dāng)一個文件的chunkserver比較繁忙的時候,它可以去寫另一個。</P>
<P style="TEXT-INDENT: 2em">6.2現(xiàn)實的集群</P>
<P style="TEXT-INDENT: 2em">我們選擇在google內(nèi)部使用的兩個集群進行測試作為相似的那些集群的一個代表。集群A主要用于100多個工程的日常研發(fā)。它會從數(shù)TB的數(shù)據(jù)中讀取數(shù)MB的數(shù)據(jù),對這些數(shù)據(jù)進行轉(zhuǎn)化或者分析,然后將結(jié)果再寫回集群。集群B主要用于產(chǎn)品數(shù)據(jù)處理。它上面的任務(wù)持續(xù)時間更長,持續(xù)地在生成和處理數(shù)TB的數(shù)據(jù)集合,只是偶爾可能需要人為的參與。在這兩種情況下,任務(wù)都是由分布在多個機器上的很進程組成,它們并行的讀寫很多文件。 </P>
<P style="TEXT-INDENT: 2em">6.2.1存儲</P>
<P style="TEXT-INDENT: 2em"><A href="http://blog.chinaunix.nethttp://blog.chinaunix.net/attachment/201101/20/25098298_1295515659fOeb.png" target=_blank><IMG src="http://blog.chinaunix.nethttp://blog.chinaunix.net/attachment/201101/20/25098298_1295515659fOeb.png" border=0 ; .load="imgResize(this, 650);"></A></P>
<P style="TEXT-INDENT: 2em"> 正如表中前5個字段所展示的,兩個集群都有數(shù)百個chunkserver,支持TB級的硬盤空間,空間已經(jīng)被充分使用但還沒全滿。已用的空間包含chunk的所有副本。通常文件存在三個副本,因此這兩個集群實際分別存儲了18TB和52TB的數(shù)據(jù)。 </P>
<P style="TEXT-INDENT: 2em">這兩個集群的文件數(shù)目很接近,盡管B集群有大量的死文件(那些已經(jīng)被刪除或者被新版本文件所替換但空間還沒有被釋放的文件)。而且它具有更多的trunk,因為它上面的文件通常更大。</P>
<P style="TEXT-INDENT: 2em"> 6.2.2元數(shù)據(jù)</P>
<P style="TEXT-INDENT: 2em">所有的Chunkserver總共存儲了數(shù)十G的元數(shù)據(jù),大部分是用戶數(shù)據(jù)的64kb塊的校驗和。Chunkserver上唯一的其他的元數(shù)據(jù)就是4.5節(jié)討論的chunk的版本號。</P>
<P style="TEXT-INDENT: 2em"> 保存在master上的元數(shù)據(jù)要更小一些,只有數(shù)十MB,平均下來每個文件只有100來個字節(jié)。這也剛好符合我們的master的內(nèi)存不會成為實際中系統(tǒng)容量限制的料想。每個文件的元數(shù)據(jù)主要是以前綴壓縮格式存儲的文件名稱。還有一些其他的元數(shù)據(jù)比如文件所有者,權(quán)限,文件到chunk的映射以及chunk的當(dāng)前版本。另外對于每個chunk我們還存儲了當(dāng)前的副本位置以及用于實現(xiàn)寫時復(fù)制的引用計數(shù)。</P>
<P style="TEXT-INDENT: 2em"> 每個獨立的server(chunkserver和master)只有50-100MB的元數(shù)據(jù)。因此,恢復(fù)是很快的:在server可以應(yīng)答查詢前只需要花幾秒鐘的時間就可以把它們從硬盤上讀出來。然而,master的啟動可能要慢一些,通常還需要30-60秒從所有的chunkserver獲得chunk的位置信息。</P>
<P style="TEXT-INDENT: 2em"> 6.2.3讀寫速率</P>
<P style="TEXT-INDENT: 2em"><A href="http://blog.chinaunix.nethttp://blog.chinaunix.net/attachment/201101/20/25098298_1295515702yJA8.png" target=_blank><IMG src="http://blog.chinaunix.nethttp://blog.chinaunix.net/attachment/201101/20/25098298_1295515702yJA8.png" border=0 ; .load="imgResize(this, 650);"></A></P>
<P style="TEXT-INDENT: 2em"> 表3展示了不同時期的讀寫速率。進行這些測量時,兩個集群都已經(jīng)運行了大約一周(為了更新到最新版本的GFS,這兩個集群被重啟過)。</P>
<P style="TEXT-INDENT: 2em"></P>
<P style="TEXT-INDENT: 2em">從啟動開始看,平均寫速率小于30MB/s。當(dāng)我們進行這些測量時,集群B正在以100MB/s的速率進行密集的寫操作,同時產(chǎn)生了300MB/s的網(wǎng)絡(luò)負載,因為寫操作將會傳給3個副本。</P>
<P style="TEXT-INDENT: 2em"> 讀速率要遠高于寫速率。正如我們料想的那樣,整個工作負載組成中,多要多于寫。這兩個集群都處在繁重的讀活動中。尤其是,A已經(jīng)在過去的一個星期中維持了580MB/s的讀速率。它的網(wǎng)絡(luò)配置可以支持750MB/s,因此它已經(jīng)充分利用了資源。B集群可支持1300 MB/s的峰值讀速率,但是應(yīng)用只使用了380 MB/s。</P>
<P style="TEXT-INDENT: 2em"> 6.2.4 master負載</P>
<P style="TEXT-INDENT: 2em">表3也表明發(fā)送給master的操作速率大概是每秒200-500個操作。Master可以輕易的處理這個級別的速率,因此對于這些工作負載來說,它不會成為瓶頸。</P>
<P style="TEXT-INDENT: 2em"> 在早期版本的GFS中,master偶爾會成為某些工作負載的瓶頸。為了查找文件,花費大量的時間在巨大的目錄(包含上千萬的文件)中進行線性掃描。因此,我們改變了master的數(shù)據(jù)結(jié)構(gòu),使之可以在名字空間內(nèi)進行有效的二分搜索,F(xiàn)在它可以簡單的支持每秒上千次的文件訪問。如果必要的話,我們還可以進一步的在名字空間數(shù)據(jù)結(jié)構(gòu)前端提供名字查找緩存。</P>
<P style="TEXT-INDENT: 2em"> 6.2.5 恢復(fù)時間</P>
<P style="TEXT-INDENT: 2em">一臺Chunkserver失敗后,它上面的那些chunk的副本數(shù)就會降低,必須進行clone以維持正常的副本數(shù);謴(fù)這些chunk的時間取決于資源的數(shù)量。在一個實驗中,我們關(guān)閉集群B中的一個chunkserver。該chunkserver大概有15000個chunk,總共600GB的數(shù)據(jù)。為減少對于應(yīng)用程序的影響以及為調(diào)度決策提供余地,我們的默認參數(shù)設(shè)置將集群的并發(fā)clone操作限制在91個(占chunkserver個數(shù)的40%),同時每個clone操作最多可以消耗6.25MB/s(50Mbps)。所有的chunk在23.2分鐘內(nèi)被恢復(fù),備份速率是440MB/s。</P>
<P style="TEXT-INDENT: 2em"> 在另一個實驗中,我們關(guān)掉了兩個chunkserver,每個具有16000個chunk,660GB的數(shù)據(jù)。這次失敗使得266個chunk降低到了一個副本,但是兩分鐘內(nèi),它們就恢復(fù)到了至少2個副本,這樣就讓集群能夠容忍另一個chunkserver發(fā)生失敗,而不產(chǎn)生數(shù)據(jù)丟失。</P>
<P style="TEXT-INDENT: 2em"> 6.3 工作負載剖析</P>
<P style="TEXT-INDENT: 2em">在這一節(jié),我們將繼續(xù)在兩個新的集群上對工作負載進行細致的對比分析。集群X是用于研究開發(fā)的,集群Y是用于產(chǎn)品數(shù)據(jù)處理。</P>
<P style="TEXT-INDENT: 2em">6.3.1 方法和說明</P>
<P style="TEXT-INDENT: 2em">這些結(jié)果只包含了客戶端產(chǎn)生的請求,因此它們反映了應(yīng)用程序的對整個文件系統(tǒng)的工作負載。并不包含為了執(zhí)行客戶端的請求進行的server間的請求,或者是內(nèi)部的后臺活動,比如寫推送或者是重平衡。</P>
<P style="TEXT-INDENT: 2em"> 對于IO操作的統(tǒng)計是從GFS的server的PRC請求日志中重新構(gòu)建出來的。比如為了增加并行性,GFS客戶端代碼可能將一個讀操作拆分為多個RPC請求,我們通過它們推斷出原始請求。因為我們的訪問模式高度的程式化,希望每個錯誤都可以出現(xiàn)在日志中。應(yīng)用程序顯式的記錄可以提供更精確的數(shù)據(jù),但是重新編譯以及重啟正在運行中的客戶端在邏輯上是不可能這樣做的。而且由于機器數(shù)很多,收集這些數(shù)據(jù)也會變得很笨重。</P>
<P style="TEXT-INDENT: 2em"> 需要注意的是,不能將我們的工作負載過于一般化。因為GFS和應(yīng)用程序是由google完全控制的,應(yīng)用程序都是針對GFS進行專門優(yōu)化的,同時GFS也是專門為這些應(yīng)用而設(shè)計的。這種相互的影響可能也存在于一般的文件系統(tǒng)及其應(yīng)用程序中,但是這種影響可能并不像我們上面所描述的那樣。</P>
<P style="TEXT-INDENT: 2em"> 6.3.2 chunkserver負載</P>
<P style="TEXT-INDENT: 2em"><A href="http://blog.chinaunix.nethttp://blog.chinaunix.net/attachment/201101/20/25098298_1295515749f0m1.png" target=_blank><IMG src="http://blog.chinaunix.nethttp://blog.chinaunix.net/attachment/201101/20/25098298_1295515749f0m1.png" border=0 ; .load="imgResize(this, 650);"></A></P>
<P style="TEXT-INDENT: 2em"> 表4展示了操作根據(jù)大小的分布。讀操作的大小表現(xiàn)出雙峰分布,小型讀操作(小于64kb)來自于那些在大量文件中查找小片數(shù)據(jù)的隨機讀客戶端,大型讀操作(超過512kb)來自于穿越整個文件的線性讀操作。</P>
<P style="TEXT-INDENT: 2em"></P>
<P style="TEXT-INDENT: 2em">集群Y中大量的讀操作沒有返回數(shù)據(jù)。我們應(yīng)用程序,尤其是在產(chǎn)品系統(tǒng)中,經(jīng)常使用文件作為生產(chǎn)者消費者隊列。生產(chǎn)者并行的往文件中append數(shù)據(jù),而消費者則從文件尾部讀數(shù)據(jù)。有時候,如果消費者超過了生產(chǎn)者,就沒有數(shù)據(jù)返回。集群X很少出現(xiàn)這種情況,因為它主要是用來進行短期數(shù)據(jù)分析,而不是長期的分布式應(yīng)用。</P>
<P style="TEXT-INDENT: 2em"> 寫操作的大小也表現(xiàn)出雙峰分布。大型的寫操作(超過256KB)通常來自于寫操作者的緩沖。那些緩沖更少數(shù)據(jù)的寫操作者,檢查點或者經(jīng)常性的同步或者簡單的數(shù)據(jù)生成組成了小型的寫操作(低于64KB)。</P>
<P style="TEXT-INDENT: 2em"> 對于記錄的append,Y集群比X集群可以看到更大的大record append比率。因為使用Y集群的產(chǎn)品系統(tǒng),針對GFS進行了更多的優(yōu)化。</P>
<P style="TEXT-INDENT: 2em"> <A href="http://blog.chinaunix.nethttp://blog.chinaunix.net/attachment/201101/20/25098298_1295515839IoGo.png" target=_blank><IMG src="http://blog.chinaunix.nethttp://blog.chinaunix.net/attachment/201101/20/25098298_1295515839IoGo.png" border=0 ; .load="imgResize(this, 650);"></A></P>
<P style="TEXT-INDENT: 2em"> 表5展示了不同大小的數(shù)據(jù)傳輸總量。對于各種操作來說,大型的操作(超過256KB)構(gòu)成了大部分的數(shù)據(jù)傳輸。但是小型(低于64KB)的讀操作雖然傳輸了比較少的數(shù)據(jù)但是在數(shù)據(jù)讀中也占據(jù)了相當(dāng)?shù)囊徊糠郑饕怯捎陔S機seek造成的。</P>
<P style="TEXT-INDENT: 2em"></P>
<P style="TEXT-INDENT: 2em">6.3.4 append與write</P>
<P style="TEXT-INDENT: 2em">記錄append操作被大量的應(yīng)用尤其是在我們的產(chǎn)品系統(tǒng)中。對于集群X來說,按字節(jié)傳輸來算,write與append的比例是108:1,根據(jù)操作數(shù)來算它們的比例是8:1。對于集群Y,比例變成了3.7:1和2.5:1。對于這兩個集群來說,它們的append操作都要比write操作大一些{操作數(shù)的比要遠大于字節(jié)數(shù)的比,說明單個的append操作的字節(jié)數(shù)要大于write}。對于集群X來說,在測量期間的記錄append操作要低一些,這可能是由其中具有特殊緩沖大小設(shè)置的應(yīng)用程序造成的。</P>
<P style="TEXT-INDENT: 2em"> 正如期望的,我們的數(shù)據(jù)變更操作處于支配地位的是追加而不是重寫{write也可能是追加}。我們測量了在主副本上的數(shù)據(jù)重寫數(shù)量。對于集群X來說,以字節(jié)大小計算的話重寫大概占了整個數(shù)據(jù)變更的0.0001%,以操作個數(shù)計算,大概小于0.0003%。對于Y集群來說,這兩個數(shù)字都是0.05%,盡管這也不算大,但是還是要高于我們的期望。結(jié)果顯示,大部分的重寫是由于錯誤或者超時導(dǎo)致的客戶端重寫而產(chǎn)生的。它們并不是工作負載的一部分,而是屬于重試機制。</P>
<P style="TEXT-INDENT: 2em"> 6.3.4 master負載</P>
<P style="TEXT-INDENT: 2em"><A href="http://blog.chinaunix.nethttp://blog.chinaunix.net/attachment/201101/20/25098298_1295515885ZBq8.png" target=_blank><IMG src="http://blog.chinaunix.nethttp://blog.chinaunix.net/attachment/201101/20/25098298_1295515885ZBq8.png" border=0 ; .load="imgResize(this, 650);"></A></P>
<P style="TEXT-INDENT: 2em"> 表6展示了對于master各種請求類型的剖析。大部分請求是為了得到chunk位置以及數(shù)據(jù)變更需要的租約持有信息。</P>
<P style="TEXT-INDENT: 2em"> 可以看到集群X和Y在delete請求上的限制區(qū)別,因為集群Y上存儲的產(chǎn)品信息會周期性地生成被新版本數(shù)據(jù)所替換。這些不同被隱藏在open請求中,因為老版的數(shù)據(jù)在被寫的時候的打開操作中被隱式的刪除(類似與Unix的”w”打開模式)。</P>
<P style="TEXT-INDENT: 2em"> 查找匹配文件是一個類似于ls的模式匹配請求。不像其他的請求,它可能需要處理很大部分的名字空間,因此可能是很昂貴的。在集群Y上可以更頻繁地看到它,因為自動化的數(shù)據(jù)處理任務(wù)為了了解整個應(yīng)用程序的狀態(tài)可能需要檢查文件系統(tǒng)中的某些部分。與此相比,集群X需要更多顯式的用戶控制而且已經(jīng)提前知道所需要的文件的名稱。</P>
<P style="TEXT-INDENT: 2em"></P>
<P style="TEXT-INDENT: 2em">7.經(jīng)驗</P>
<P style="TEXT-INDENT: 2em">在構(gòu)建和部署GFS的過程中,我們總結(jié)出了很多經(jīng)驗,觀點和技術(shù)。</P>
<P style="TEXT-INDENT: 2em"> 起初,GFS只是考慮作為我們產(chǎn)品系統(tǒng)的后端文件系統(tǒng)。隨著時間的推移,開始在研究和開發(fā)中使用。一開始它基本不支持像權(quán)限,quota這些東西,但是現(xiàn)在它們都已經(jīng)有了。產(chǎn)品系統(tǒng)是很容易控制的,但是用戶卻不是。因此需要更多的設(shè)施來避免用戶間的干擾。</P>
<P style="TEXT-INDENT: 2em"> 我們最大的問題是硬盤和linux相關(guān)性。我們的很多硬盤聲稱支持各種IDE協(xié)議版本的linux驅(qū)動,但是實際上它們只能在最近的一些上才能可靠的工作。因此如果協(xié)議版本如果相差不大,硬盤大多數(shù)情況下都可以工作,但是有時候這種不一致會使得驅(qū)動和內(nèi)核在硬盤狀態(tài)上產(chǎn)生分歧。由于內(nèi)核的問題,這將會導(dǎo)致數(shù)據(jù)被默默的污染。這個問題使得我們使用校驗和來檢測數(shù)據(jù)污染,如果出現(xiàn)這種情況,我們就需要修改內(nèi)核來處理這種協(xié)議不一致的情況。</P>
<P style="TEXT-INDENT: 2em">之前,由于linux2.2內(nèi)核的fsync()的花費,我們也碰到過一些問題。它的花費是與文件大小而不是被修改部分的大小相關(guān)的。這對于我們大的操作日志會是一個問題,尤其是在我們實現(xiàn)檢查點之前。我們通過改用同步寫來繞過了這個問題,最后遷移到Linux2.4來解決了它。</P>
<P style="TEXT-INDENT: 2em"> 另一個由于linux產(chǎn)生的問題是與讀寫鎖相關(guān)的。在一個地址空間里的線程在從硬盤中讀頁數(shù)據(jù)(讀鎖)或者在mmap調(diào)用中修改地址空間(寫鎖)的時候,必須持有一個讀寫鎖。在系統(tǒng)負載很高,產(chǎn)生資源瓶頸或者出現(xiàn)硬件失敗時,我們碰到了瞬態(tài)的超時。最后,我們發(fā)現(xiàn)當(dāng)磁盤讀寫線程處理前面映射的數(shù)據(jù)時,這個鎖阻塞了網(wǎng)絡(luò)線程將新的數(shù)據(jù)映射到內(nèi)存。由于我們的工作瓶頸主要是在網(wǎng)絡(luò)帶寬而不是內(nèi)存帶寬,因此我們通過使用pread()加上額外的開銷替代mmap()繞過了這個問題。</P>
<P style="TEXT-INDENT: 2em"> 盡管出現(xiàn)了一些問題,linux代碼的可用性幫助了我們探索和理解系統(tǒng)的行為。在適當(dāng)?shù)臅r機,我們也會改進內(nèi)核并與開源社區(qū)共享這些變化。</P>
<P style="TEXT-INDENT: 2em"> 8.相關(guān)工作</P>
<P style="TEXT-INDENT: 2em">像其他的大型分布式文件系統(tǒng)比如AFS,GFS提供了一個本地的獨立名字空間,使得數(shù)據(jù)可以為了容錯或者負載平衡而透明的移動。但與AFS不同的是,為了提升整體的性能和容錯能力,GFS將文件數(shù)據(jù)在多個存儲服務(wù)器上存儲,這點更類似于xFS或者Swift。</P>
<P style="TEXT-INDENT: 2em"> 硬盤是相對便宜的,而且與復(fù)雜的RAID策略相比,副本策略更簡單。由于GFS完全采用副本策略進行冗余因此它會比xFS或者Swift消耗更多的原始存儲。</P>
<P style="TEXT-INDENT: 2em">與AFS,xFS,Frangipani,Intermezzo這些系統(tǒng)相比,GFS在文件系統(tǒng)接口下并不提供任何緩存。我們的目標工作負載類型對于通常的單應(yīng)用程序運行模式來說,基本上是不可重用的,因為這種模式通常需要讀取大量數(shù)據(jù)集合或者在里面進行隨機的seek,而每次只讀少量的數(shù)據(jù)。</P>
<P style="TEXT-INDENT: 2em">一些分布式文件系統(tǒng)比如xFS,Frangipani,Minnesota’s GFS和GPFS刪除了中央服務(wù)節(jié)點,依賴于分布式的算法進行一致性和管理。我們選擇中央化測量是為了簡化設(shè)計增加可靠性,獲取靈活性。尤其是,一個中央化的master更容易實現(xiàn)復(fù)雜的chunk放置和備份策略,因為master具有大部分的相關(guān)信息以及控制了它們的改變。我們通過讓master狀態(tài)很小以及在其他機器上進行備份來解決容錯。當(dāng)前通過影子master機制提供可擴展性和可用性。對于master狀態(tài)的更新,通過append到write-ahead 日志里進行持久化。因此我們可以通過類似于Harp里的主copy模式來提供一個比我們當(dāng)前模式具有更強一致性的高可用性。</P>
<P style="TEXT-INDENT: 2em"> 我們未來將解決類似于Lustre的一個問題:大量客戶端的整體性能。然而我們通過專注于我們自己的需求而不是構(gòu)建一個POSIX兼容文件系統(tǒng)來簡化了這個問題。另外,GFS加速不可靠組件的數(shù)量是很大的,因此容錯是我們設(shè)計的中心。</P>
<P style="TEXT-INDENT: 2em"> GFS很類似于NASD架構(gòu)。但是NASD是基于網(wǎng)絡(luò)連接的硬盤驅(qū)動器,GFS則使用普通機器作為chunkserver。與NASD不同,chunkserver在需要時分配固定大小的chunk,而沒有使用變長對象。此外,GFS還實現(xiàn)了諸如重平衡,副本,產(chǎn)品環(huán)境需要的快速恢復(fù)。</P>
<P style="TEXT-INDENT: 2em"> 不像Minnesota’s GFS和NASD,我們并沒有尋求改變存儲設(shè)備的模型。我們更專注于解決使用現(xiàn)有商品化組件組成的復(fù)雜分布式系統(tǒng)的日常的數(shù)據(jù)處理需求。</P>
<P style="TEXT-INDENT: 2em"> 通過在生產(chǎn)者消費者隊列中使用原子record append操作解決了與分布式操作系統(tǒng)River的類似問題。River使用基于內(nèi)存的跨機器分布式隊列以及小心的數(shù)據(jù)流控制來解決這個問題,而GFS只使用了一個可以被很多生產(chǎn)者append數(shù)據(jù)的文件。River模型支持m to n的分布式隊列,但是缺乏容錯,GFS目前只支持m to 1。多個消費者可以讀取相同文件,但是它們必須協(xié)調(diào)好對輸入負載進行劃分(各自處理不相交的一部分)。</P>
<P style="TEXT-INDENT: 2em"> 9.總結(jié)</P>
<P style="TEXT-INDENT: 2em">GFS包含了那些在商品化硬件上支持大規(guī)模數(shù)據(jù)處理的必要特征。盡管某些設(shè)計決定與我們特殊的應(yīng)用類型相關(guān),但是可以應(yīng)用在具有類似需求和特征的數(shù)據(jù)處理任務(wù)中。</P>
<P style="TEXT-INDENT: 2em"> 針對我們當(dāng)前的應(yīng)用負載類型,我們重新審視傳統(tǒng)的文件系統(tǒng)的一些假設(shè)。我們的審視,使得我們的設(shè)計中產(chǎn)生了一些與之根本不同的觀點。我們將組件失敗看做常態(tài)而不是異常,為經(jīng)常進行的在大文件上的append進行優(yōu)化,然后是讀(通常是順序的),為了改進整個系統(tǒng)我們擴展并且放松了標準文件系統(tǒng)接口。</P>
<P style="TEXT-INDENT: 2em"> 我們的系統(tǒng)通過監(jiān)控,備份關(guān)鍵數(shù)據(jù),快速和自動恢復(fù)來提供容錯。Chunk備份使得我們可以容忍chunkserver的失敗。這些經(jīng)常性的失敗,驅(qū)動了一個優(yōu)雅的在線修復(fù)機制的產(chǎn)生,它周期性地透明的進行修復(fù)盡快的恢復(fù)那些丟失的副本。另外,我們通過使用校驗和來檢測數(shù)據(jù)損壞,當(dāng)系統(tǒng)中硬盤數(shù)目很大的時候,這種損壞變得很正常。</P>
<P style="TEXT-INDENT: 2em"> 我們的設(shè)計實現(xiàn)了對于很多執(zhí)行大量任務(wù)的并發(fā)讀者和寫者的高吞吐率。通過從數(shù)據(jù)傳輸中分離文件系統(tǒng)控制,我們來實現(xiàn)這個目標,讓master來處理文件系統(tǒng)控制,數(shù)據(jù)傳輸則直接在chunkserver和客戶端之間進行。通過增大chunk的大小以及chunk的租約機制,降低了master在普通操作中的參與。這使中央的master不會成為瓶頸。我們相信在當(dāng)前網(wǎng)絡(luò)協(xié)議棧上的改進將會提供客戶端寫出速率的限制。</P>
<P style="TEXT-INDENT: 2em"> GFS成功地滿足了我們的存儲需求,同時除了作為產(chǎn)品數(shù)據(jù)處理平臺外,還作為研發(fā)的存儲平臺而被廣泛使用。它是一個使我們可以持續(xù)創(chuàng)新以及面對整個web的海量數(shù)據(jù)挑戰(zhàn)的重要工具。</P>
<P style="TEXT-INDENT: 2em">致謝</P>
<P style="TEXT-INDENT: 2em">……</P></DIV>
<DIV>本文轉(zhuǎn)自:<A href="http://duanple.blog.163.com/blog/static/7097176720109145829346/" target=_blank>http://duanple.blog.163.com/blog/static/7097176720109145829346/</A></DIV>
<DIV> </DIV>
<DIV>英文原文地址: <A href="http://labs.google.com/papers/gfs.html" target=_blank>http://labs.google.com/papers/gfs.html</A></DIV>
<DIV>附英文原文下載:<A href="http://blog.chinaunix.nethttp://blog.chinaunix.net/attachment/attach/25/09/82/9825098298af6ea9b7157fa5a93eb709731a8a577c.pdf" target=_blank><IMG src="http://blog.chinaunix.net/blog/image/attachicons/common.gif" align=absMiddle border=0> gfs-sosp2003.pdf </A> </DIV>
<DIV> </DIV> |
|