大數據理論篇HDFS的基石——Google File System

Google File System

但凡是要開始講大數據的,都繞不開最初的Google三駕馬車:Google File System(GFS), MapReduce,BigTable。node

爲這一切的基礎的Google File System,不但沒有任何倒臺的跡象,還在不斷的演化,事實上支撐着Google這個龐大的互聯網公司的一切計算。web

如下是原文內容,內容較長,建議詳細閱讀。算法

摘要

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

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 設計預期

​ 在設計知足咱們需求的文件系統時候,咱們的設計目標既有機會、又有挑戰。以前咱們已經提到了一些須要關注的關鍵點,這裏咱們將設計的預期目標的細節展開討論。
​ 系統由許多廉價的普通組件組成,組件失效是一種常態。系統必須持續監控自身的狀態,它必須將組件失效做爲一種常態,可以迅速地偵測、冗餘並恢復失效的組件。系統存儲必定數量的大文件。咱們預期會有幾百萬文件,文件的大小一般在 100MB 或者以上。數個 GB大小的文件也是廣泛存在,而且要可以被有效的管理。系統也必須支持小文件,可是不須要針對小文件作專門的優化。緩存

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

2.2 接口

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

2.3 架構

​ 一個 GFS 集羣包含一個單獨的 Master 節點 3 、多臺 Chunk 服務器,而且同時被多個客戶端訪問,如圖 1所示。全部的這些機器一般都是普通的 Linux 機器,運行着用戶級別(user-level)的服務進程。咱們能夠很容易的把 Chunk 服務器和客戶端都放在同一臺機器上,前提是機器資源容許,而且咱們可以接受不可靠的應用程序代碼帶來的穩定性下降的風險。服務器

​ GFS 存儲的文件都被分割成固定大小的 Chunk。在 Chunk 建立的時候,Master 服務器會給每一個 Chunk 分配一個不變的、全球惟一的 64 位的 Chunk 標識。Chunk 服務器把 Chunk 以 Linux 文件的形式保存在本地硬盤上,而且根據指定的 Chunk 標識和字節範圍來讀寫塊數據。出於可靠性的考慮,每一個塊都會複製到多個塊服務器上。缺省狀況下,咱們使用 3 個存儲複製節點,不過用戶能夠爲不一樣的文件命名空間設定不一樣的複製級別。
​ Master 節點管理全部的文件系統元數據。這些元數據包括名字空間、訪問控制信息、文件和 Chunk 的映射信息、以及當前 Chunk 的位置信息。Master 節點還管理着系統範圍內的活動,好比,Chunk 租用管理 、孤兒 Chunk 的回收、以及 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 服務器存儲 3 種主要類型的元數據,包括:文件和 Chunk 的命名空間、文件和 Chunk 的對應關係、
每一個 Chunk 副本的存放地點。全部的元數據都保存在 Master 服務器的內存中。前兩種類型的元數據同時也會以記錄變動日誌的方式記錄在操做系統的系統日誌文件中,日誌文件存儲在本地磁盤上,同時日誌會被複制到其它的遠程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 服務器的狀態。
​ 最初設計時,咱們試圖把 Chunk 的位置信息持久的保存在 Master 服務器上,可是後來咱們發如今啓動的
時候輪詢 Chunk 服務器,以後按期輪詢更新的方式更簡單。這種設計簡化了在有 Chunk 服務器加入集羣、離
開集羣、改名、失效、以及重啓的時候,Master 服務器和 Chunk 服務器數據同步的問題。在一個擁有數百臺
服務器的集羣中,這類事件會頻繁的發生。
​ 能夠從另一個角度去理解這個設計決策:只有 Chunk 服務器才能最終肯定一個 Chunk 是否在它的硬盤
上。咱們從沒有考慮過在 Master 服務器上維護一個這些信息的全局視圖,由於 Chunk 服務器的錯誤可能會導
致 Chunk 自動消失(好比,硬盤損壞了或者沒法訪問了),亦或者操做人員可能會重命名一個 Chunk 服務器。

2.6.3 操做日誌

​ 操做日誌包含了關鍵的元數據變動歷史記錄。這對 GFS 很是重要。這不只僅是由於操做日誌是元數據惟
一的持久化存儲記錄,它也做爲判斷同步操做順序的邏輯時間基線。文件和 Chunk,連同它們的版本(參考
4.5 節),都由它們建立的邏輯時間惟一的、永久的標識。
​ 操做日誌很是重要,咱們必須確保日誌文件的完整,確保只有在元數據的變化被持久化後,日誌纔對客
戶端是可見的。不然,即便 Chunk 自己沒有出現任何問題,咱們仍有可能丟失整個文件系統,或者丟失客戶
端最近的操做。因此,咱們會把日誌複製到多臺遠程機器,而且只有把相應的日誌記錄寫入到本地以及遠程
機器的硬盤後,纔會響應客戶端的操做請求。Master 服務器會收集多個日誌記錄後批量處理,以減小寫入磁
盤和複製對系統總體性能的影響。
​ Master 服務器在災難恢復時,經過重演操做日誌把文件系統恢復到最近的狀態。爲了縮短 Master 啓動的
時間,咱們必須使日誌足夠小。Master 服務器在日誌增加到必定量時對系統狀態作一次 Checkpoint ,將所
有的狀態數據寫入一個 Checkpoint 文件。在災難恢復的時候,Master 服務器就經過從磁盤上讀取這Checkpoint 文件,以及重演 Checkpoint 以後的有限個日誌文件就可以恢復系統。Checkpoint 文件以壓縮 B-樹形勢的數據結構存儲,能夠直接映射到內存,在用於命名空間查詢時無需額外的解析。這大大提升了恢復速度,加強了可用性。
​ 因爲建立一個 Checkpoint 文件須要必定的時間,因此 Master 服務器的內部狀態被組織爲一種格式,這種格式要確保在 Checkpoint 過程當中不會阻塞正在進行的修改操做。Master 服務器使用獨立的線程切換到新的日誌文件和建立新的 Checkpoint 文件。新的 Checkpoint 文件包括切換前全部的修改。對於一個包含數百萬個文件的集羣,建立一個 Checkpoint 文件須要 1 分鐘左右的時間。建立完成後,Checkpoint 文件會被寫入在本地和遠程的硬盤裏。
​ Master 服務器恢復只須要最新的 Checkpoint 文件和後續的日誌文件。舊的 Checkpoint 文件和日誌文件可
以被刪除,可是爲了應對災難性的故障 ,咱們一般會多保存一些歷史文件。Checkpoint 失敗不會對正確性產
生任何影響,由於恢復功能的代碼能夠檢測並跳過沒有完成的 Checkpoint 文件。

2.7 一致性模型

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

2.7.1 GFS 一致性保障機制

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

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

數據修改操做分爲寫入或者記錄追加兩種。寫入操做把數據寫在應用程序指定的文件偏移位置上。即便

有多個修改操做並行執行時,記錄追加操做至少能夠把數據原子性的追加到文件中一次,可是偏移位置是由
GFS 選擇的(3.3 章) 。GFS 返回給客戶端一個偏移量,表示了包含了寫入記錄的、已定義的 region 的起點。
另外,GFS 可能會在文件中間插入填充數據或者重複記錄。這些數據佔據的文件 region 被認定是不一致的,
這些數據一般比用戶數據小的多。通過了一系列的成功的修改操做以後,GFS 確保被修改的文件 region 是已定義的,而且包含最後一次修改操做寫入的數據。GFS 經過如下措施確保上述行爲:

(a) 對 Chunk 的全部副本的修改操做順序一致(3.1章),

(b)使用 Chunk 的版本號來檢測副本是否由於它所在的 Chunk 服務器宕機(4.5 章)而錯過了修改操做
而致使其失效。

​ 失效的副本不會再進行任何修改操做,Master 服務器也再也不返回這個 Chunk 副本的位置信息給客戶端。它們會被垃圾收集系統儘快回收。因爲 Chunk 位置信息會被客戶端緩存,因此在信息刷新前,客戶端有可能從一個失效的副本讀取了數據。在緩存的超時時間和文件下一次被打開的時間之間存在一個時間窗,文件再次被打開後會清除緩存中與該文件有關的全部 Chunk 位置信息。並且,因爲咱們的文件大多數都是隻進行追加操做的,因此,一個失效的副本一般返回一個提早結束的 Chunk 而不是過時的數據。當一個 Reader 16 從新嘗試並聯絡 Master 服務器時,它就會馬上獲得最新的 Chunk 位置信息。
​ 即便在修改操做成功執行很長時間以後,組件的失效也可能損壞或者刪除數據。GFS 經過 Master 服務器和全部 Chunk 服務器的按期「握手」來找到失效的 Chunk 服務器,而且使用 Checksum 來校驗數據是否損壞(5.2 章)。一旦發現問題,數據要儘快利用有效的副本進行恢復(4.3 章)。只有當一個 Chunk 的全部副本在 GFS 檢測到錯誤並採起應對措施以前所有丟失,這個 Chunk 纔會不可逆轉的丟失。在通常狀況下 GFS 的反應時間是幾分鐘。即便在這種狀況下,Chunk 也只是不可用了,而不是損壞了:應用程序會收到明確的錯誤信息而不是損壞的數據。

2.7.2 程序的實現

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

3 系統交互

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

3.1 租約(lease) 和變動順序

​ 變動是一個會改變 Chunk 內容或者元數據的操做,好比寫入操做或者記錄追加操做。變動操做會在 Chunk
的全部副本上執行。咱們使用租約(lease)機制來保持多個副本間變動順序的一致性。Master 節點爲 Chunk的一個副本創建一個租約,咱們把這個副本叫作主 Chunk。主 Chunk 對 Chunk 的全部更改操做進行序列化。
全部的副本都聽從這個序列進行修改操做。所以,修改操做全局的順序首先由 Master 節點選擇的租約的順序
決定,而後由租約中主 Chunk 分配的序列號決定。
​ 設計租約機制的目的是爲了最小化 Master 節點的管理負擔。租約的初始超時設置爲 60 秒。不過,只要
Chunk 被修改了,主 Chunk 就能夠申請更長的租期,一般會獲得 Master 節點的確認並收到租約延長的時間。
這些租約延長請求和批准的信息一般都是附加在 Master 節點和 Chunk 服務器之間的心跳消息中來傳遞。有時
Master 節點會試圖提早取消租約(例如,Master 節點想取消在一個已經被更名的文件上的修改操做)。即便
Master節點和主Chunk失去聯繫,它仍然能夠安全地在舊的租約到期後和另一個Chunk副本簽定新的租約。
在圖 2 中,咱們依據步驟編號,展示寫入操做的控制流程。

​ 客戶機向 Master 節點詢問哪個 Chunk 服務器持有當前的租約,以及其它副本的位置。若是沒有一個
Chunk 持有租約,Master 節點就選擇其中一個副本創建一個租約(這個步驟在圖上沒有顯示)。
​ Master 節點將主 Chunk 的標識符以及其它副本(又稱爲 secondary 副本、二級副本)的位置返回給客戶機。客戶機緩存這些數據以便後續的操做。只有在主 Chunk 不可用,或者主 Chunk 回覆信息代表它已再也不持有租約的時候,客戶機才須要從新跟 Master 節點聯繫。
​ 客戶機把數據推送到全部的副本上。客戶機能夠以任意的順序推送數據。Chunk 服務器接收到數據並保存在它的內部 LRU 緩存中,一直到數據被使用或者過時交換出去。因爲數據流的網絡傳輸負載很是高,經過分離數據流和控制流,咱們能夠基於網絡拓撲狀況對數據流進行規劃,提升系統性能,而不用去理會哪一個Chunk 服務器保存了主 Chunk。3.2 章節會進一步討論這點。
​ 當全部的副本都確認接收到了數據,客戶機發送寫請求到主 Chunk 服務器。這個請求標識了早前推送到全部副本的數據。主 Chunk 爲接收到的全部操做分配連續的序列號,這些操做可能來自不一樣的客戶機,序列號保證了操做順序執行。它以序列號的順序把操做應用到它本身的本地狀態中(alex 注:也就是在本地執行這些操做,這句話按字面翻譯有點費解,也許應該翻譯爲「它順序執行這些操做,並更新本身的狀態」)。主 Chunk 把寫請求傳遞到全部的二級副本。每一個二級副本依照主 Chunk 分配的序列號以相同的順序執行這些操做。
​ 全部的二級副本回復主 Chunk,它們已經完成了操做。
​ 主 Chunk 服務器 20 回覆客戶機。任何副本產生的任何錯誤都會返回給客戶機。在出現錯誤的狀況下,寫
入操做可能在主 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 快照

​ 快照操做幾乎能夠瞬間完成對一個文件或者目錄樹(「源」)作一個拷貝,而且幾乎不會對正在進行的其它操做形成任何干擾。咱們的用戶能夠使用快照迅速的建立一個巨大的數據集的分支拷貝(並且常常是遞歸的拷貝拷貝),或者是在作實驗性的數據操做以前,使用快照操做備份當前狀態,這樣以後就能夠輕鬆的提交或者回滾到備份時的狀態。
​ 就像 AFS(alex 注:AFS,即 Andrew File System,一種分佈式文件系統),咱們用標準的 copy-on-write技術實現快照。當 Master 節點收到一個快照請求,它首先取消做快照的文件的全部 Chunk 的租約。這個措施保證了後續對這些 Chunk 的寫操做都必須與 Master 交互以找到租約持有者。這就給 Master 節點一個率先建立
Chunk 的新拷貝的機會。
​ 租約取消或者過時以後,Master 節點把這個操做以日誌的方式記錄到硬盤上。而後,Master 節點經過複製源文件或者目錄的元數據的方式,把這條日誌記錄的變化反映到保存在內存的狀態中。新建立的快照文件和源文件指向徹底相同的 Chunk 地址。
​ 在快照操做以後,當客戶機第一次想寫入數據到 Chunk C,它首先會發送一個請求到 Master 節點查詢當前的租約持有者。Master節點注意到Chunk C的引用計數超過了1 22 。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 的鏈接。
​ 在對 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 服務器的 24 。垃圾回收提供了一致的、可靠的清除無用副本的方法。第二,垃圾回收把
存儲空間的回收操做合併到 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 掉進程來關閉服務器。客戶
機和其它的服務器會感受到系統有點顛簸 25 ,正在發出的請求會超時,須要從新鏈接到重啓後的服務器,而後
重試這個請求。6.6.2 章節記錄了實測的啓動時間。

5.1.2 Chunk 複製

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

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 服務器上,
Checksum 的查找和比較不須要 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,咱們都
保存了當前的副本位置以及對它的引用計數,這個引用計數用於實現寫時拷貝(即 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 請求推導出原始的讀
操做。由於咱們的訪問模式是高度程式化,因此咱們認爲任何不符合的數據都是偏差 28 。應用程序若是可以記
錄更詳盡的日誌,就有可能提供更準確的診斷數據;可是爲了這個目的去從新編譯和從新啓動數千個正在運
行的客戶機是不現實的,並且從那麼多客戶機上收集結果也是個繁重的工做。
​ 應該避免從咱們的工做負荷數據中過分的概括出廣泛的結論 29 。由於Google徹底控制着GFS和使用GFS
的應用程序,因此,應用程序都針對 GFS 作了優化,同時,GFS 也是爲了這些應用程序而設計的。這樣的相
互做用也可能存在於通常程序和文件系統中,可是在咱們的案例中這樣的做用影響可能更顯著。

表 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 30 。咱們只關注用普通的設備來解
決很是複雜的分佈式系統平常的數據處理。
​ 咱們經過原子的記錄追加操做實現了生產者-消費者隊列,這個問題相似 River[2]中的分佈式隊列。River
使用的是跨主機的、基於內存的分佈式隊列,爲了實現這個隊列,必須仔細控制數據流;而 GFS 採用能夠被
生產者併發追加記錄的持久化的文件的方式實現。River 模式支持 m-到-n 的分佈式隊列,可是缺乏由持久化
存儲提供的容錯機制,GFS 只支持 m-到-1 的隊列。多個消費者能夠同時讀取一個文件,可是它們輸入流的區
間必須是對齊的。

9 結束語

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

更多Flink,Kafka,Spark等相關技術博文,科技資訊,歡迎關注實時流式計算 公衆號後臺回覆 「電子書」 下載300頁Flink實戰電子書

相關文章
相關標籤/搜索