GFS架構剖析

摘要:GFS在設計上有很多值得學習的地方,最近重讀了一下GFS的設計論文,試圖從架構設計的角度對GFS進行剖析,希望可以借鑒一些分布式系統的設計思路。

1 設計約束

? ? ? ?GFS是針對Google迅速增長的數據處理需求的定制化文件系統,它的設計初衷就不是提供兼容POSIX的通用文件系統。這使得它沒有太多束縛,能夠很靈活的進行方案設計,以最簡單的方式實現Google應用程序需要的特性。?

? ? ? ?文件通常被用于“生產者-消費者” 隊列,或者其它多路文件合并操作。通常會有數百個生產者,每個生產者進程運行在一臺機器上,同時對一個文件進行追加操作。使用最小的同步開銷來實現的原子的多路追加數據操作是必不可少的。文件可以在稍后讀取,或者是消費者在追加的操作的同時讀取文件。

? ? ? ?GFS客戶程序訪問特征:

? ? ? ?1)文件通常比較大。

? ? ? ?2)數據一旦被寫入后,文件就很少會被修改。

? ? ? ?3)對文件的修改通常都是在文件尾部追加數據,而不是覆蓋原有數據。

? ? ? ?4)系統的主要工作負載是大規模的流式讀取和小規模的隨機讀取。

? ? ? ?5)高性能的穩定網絡帶寬比低延遲重要。

? ? ? ?組件失效被認為是常態事件,而不是意外事件。GFS是一個典型的分布式系統,系統由許多廉價的普通組件組成,組件失效是一種常態。系統必須持續監控自身的狀態,它必須將組件失效作為一種常態,能夠迅速地偵測、冗余并恢復失效的組件。

2. 架構剖析

2.1 中心服務器架構

? ? ? ?GFS選擇了中心服務器(Master)架構,相較于依賴分布式算法來保證一致性和可管理性的架構,設計更加簡單,增加了可靠性,能夠靈活擴展。?

? ? ? ?Master 節點管理所有的文件系統元數據。這些元數據包括名字空間、訪問控制信息、文件和 Chunk 的映射信息、以及當前 Chunk 的位置信息。 Master 節點還管理著系統范圍內的活動,比如, Chunk 租用管理、孤兒 Chunk的回收、以及 Chunk 在 Chunk Server之間的遷移。 Master 節點使用心跳信息周期地和每個 Chunk Server通訊,發送指令到各個Chunk Server 并接收 Chunk Server的狀態信息。

? ? ? ?由于處于中心位置的 Master 服務器保存有幾乎所有的 Chunk 相關信息,并且控制著 Chunk 的所有變更,因此,它極大地簡化了原本非常復雜的 Chunk 分配和復制策略的實現方法。通過減少 Master 服務器保存的狀態信息的數量,以及將 Master 服務器的狀態復制到其它節點來保證系統的災難冗余能力。

2.2 高可用存儲架構

? ? ? 存儲高可用方案的本質都是通過將數據復制到多個存儲設備,通過數據冗余的方式來實現高可用,其復雜性主要體現在如何應對復制延遲和中斷導致的數據不一致問題。

2.2.1 數據分散集群

? ? ? GFS中一個文件被順序分割為多個Chunk,在Chunk Server集群分散保存Chunk的多個副本,來實現存儲高可用。這是典型的“數據分散集群”架構,即數據存儲在多個分片上,有一個角色來負責執行數據分配算法,在GFS中這個工作是由Master節點完成的。Master節點負責分配Chunk存儲在哪些Chunk Server。分配算法根據各個Chunk Server的機架分布、硬盤使用率、繁忙程度等因素來執行,以達到最大化數據可靠性和可用性,最大化網絡帶寬利用率的目標。

? ? ? Chunk Server負責存儲數據,維護數據可用性,并向Master節點同步本地Chunk元數據,以方便Master節點建立文件到Chunk映射的完整視圖。另外,為了避免 Master 成為系統瓶頸,GFS設計了租約機制。租約由Master負責授權,持有Chunk租約的Chunk Server成為主節點,而其他Chunk Server成為跟隨節點。在租約有效期內,對該Chunk的寫操作由主節點來控制。

2.2.2 Write流程(數據分片和復制)

GFS寫控制流

? ? ? 1)客戶端向Master請求Chunk每個副本所在的Chunk Server,其中Primary Chunk Server持有租約。如果沒有Chunk Server持有租約,說明該Chunk最近沒有寫操作,Master會發起一個任務,按照一定的策略將Chunk的租約授權給其中一臺Chunk Server;

? ? ? 2)Master返回客戶端Primary和其它Chunk Server的位置信息,客戶端將緩存這些信息供以后使用。如果不出現故障,客戶端以后讀寫該Chunk都不需要再次請求Master;

? ? ? 3)客戶端將要追加的記錄發送到Chunk副本所在的任意一個Chunk Server,通常選擇距離最近的Chunk Server。Chunk Server在本地緩存數據,并轉發給距離最近的下一個Chunk Server,直到所有的Chunk Server都收到數據;

? ? ? 4)當所有副本都確認收到了數據后,客戶端發起一個寫請求控制命令給Primary。由于Primary可能收到多個客戶端對同一個Chunk的并發追加操作,Primary將確定這些操作的順序并寫入本地硬盤;

? ? ? 5)Primary把寫請求提交給所有的Secondary副本,每一個Secondary會根據Primary確定的順序執行寫操作;

? ? ? 6)Secondary副本成功完成后應答Primary;

? ? ? 7)Primary應答客戶端。

? ? ? 在Write流程中,寫入成功必須保證所有副本均成功,也就是說不存在類似其他分布式系統中的復制延遲問題。如果有任何副本發生錯誤,將會出現Primary寫成功但是某些Secondary不成功的情況,此時Primary給客戶端回失敗,但已經寫入數據的副本不會回滾。客戶端稍后進行重試,所有發副本會在統一的偏移地址再次寫入記錄,這會出現兩種情況:

? ? ?(1) 第一次寫入成功的副本,記錄發生重復。

? ? ?(2)?第一次寫入失敗的副本,存在填充的臟數據。在GFS的一致性模型中,這種情況是可以接受的,需要應用程序來處理。

2.2.3 容錯機制

? ? ? Master的容錯與傳統的方法類似,通過操作日志加checkpoint的方式進行,并且有一臺稱為”Shadow Master”的實時熱備。

? ? ? Master上保存了三種元數據信息:

? ? ? ?(1) 命名空間(Name Space),也就是整個文件系統的目錄結構以及chunk基本信息;

? ? ? ?(2) 文件到chunk之間的映射;

? ? ? ?(3) Chunk副本的位置信息,每個Chunk通常有三個副本;

? ? ? ?GFS Master的修改操作總是先記錄操作日志,然后再修改內存,當Master發生故障重啟時,可以通過磁盤中的操作日志恢復內存數據結構;另外,為了減少Master宕機恢復時間,Master會定期將內存中的數據以checkpoint文件的形式轉儲到磁盤中,從而減少回放的日志量。為了進一步提高Master的可靠性和可用性,GFS中還會執行實時熱備,所有的元數據修改操作都必須保證發送到實時熱備才算成功。遠程的實時熱備將實時接收Master發送的操作日志并在內存中回放這些元數據操作。如果Master宕機,還可以秒級切換到實時備機繼續提供服務。為了保證同一時刻只有一個Master,GFS依賴Google內部的Chubby服務進行選主操作。

? ? ? ?Master需要持久化前兩種元數據,即命令空間及文件到chunk之間的映射關系;對于第三種元數據,即Chunk副本的位置信息,Master可以選擇不進行持久化,這是因為ChunkServer維護了這些信息,即使Master發生故障,也可以在重啟時通過ChunkServer匯報來獲取。

? ? ? ?GFS采用復制多個副本的方式實現Chunk Server的容錯,每一個Chunk有多個存儲副本,分別存儲在不同的Chunk Server上。對于每一個Chunk,必須將所有的副本全部寫入成功,才視為成功寫入。如果相關的副本出現丟失或不可恢復的情況,Master自動將給副本復制到其它Chunk Server,從而確保副本保持一定的個數。

2.2.4 一致性模型

? ? ? GFS提供的一致性模型歸結為下表,

GFS一致性模型

? ? ?1)寫操作一致性

? ? ?如果串行操作成功,文件狀態是defined的。如果是并發寫成功,文件狀態是consistent but undefined。也就是說,客戶端看到的數據都是一樣的,但是可能看到的內容可能不能反映任何一個寫操作的全部內容,而是來自多個修改的混雜的數據(mingled fragments from multiple mutations)。如果失敗,結果是inconsistent的。

? ? ? consistent but undefined可能不太好理解,它實際說的是寫請求不是serializable的。舉個例子可能就清楚了。假如多個客戶端同時修改一個文件的[a,b]這個范圍的內容,其中a=64M,b=64M+1,即這兩個字節正好跨兩個chunk的邊界。某個客戶端想把內容改為”AA”,另一個客戶端想改成”BB”。 consistent but undefined是說,可能出現”AB”或”BA”的結果。這個結果并不能反映任何一個操作的全部修改。

? ? ? 2)記錄追加一致性

? ? ? 對于記錄追加,操作成功了,其結果是defined interspersed with inconsistent的。為理解這個結論,我們先看記錄追加的過程。

? ? ? 記錄追加和上述描述的寫操作流程基本類似,只是多了一步追加padding的邏輯。追加padding的目的是保證記錄追加的原子性。跨Chunk的追加可能導致非serializable的結果,所以假如文件最后一個Chunk剩余空間不夠存放本次寫操作的數據,就要把該Chunk填滿padding(備副本也要同樣地填上padding),再讓客戶端在新的Chunk上重試。在新的Chunk上(如果原Chunk剩余空間足夠,就是在原Chunk上),主副本會將數據追加到Chunk內,然后通知備副本在同樣的偏移量上追加數據。如果失敗,會有如下的結果:某些副本上的數據寫成功了,而另一些副本上沒有該數據或只有部分數據。失敗的操作會被重試。GFS不保證副本的每個字節都是一樣的,但是能夠保證數據作為一個整體被至少一次原子地寫入,而且最后寫成功的那次在各個副本上該記錄對應的偏移量都是一樣的。這就是為何前面說對于記錄追加,操作成功了,其結果是defined interspersed with inconsistent。

? ? ? GFS主要是為了追加(Append)而不是改寫(Overwrite)而設計的。一方面是因為是改寫的需求比較少,或者可以通過追加來實現,比如可以只使用GFS的追加功能構建分布式表格系統Bigtable;另一方面是因為追加的一致性模型相比改寫要更加簡單有效。考慮Chunk A的三個副本A1,A2,A3,有一個改寫操作修改了A1,A2但沒有修改A3,這樣,落到副本A3的讀操作可能讀到不正確的數據;相應地,如果有一個追加操作往A1,A2上追加了一個記錄但是追加A3失敗,那么即使讀操作落到副本A3也只是讀到過期而不是不正確的數據。如果不發生異常,追加成功的記錄在GFS的各個副本中是確定并且嚴格一致的;但是如果出現了異常,可能出現某些副本追加成功而某些副本沒有成功的情況,失敗的副本可能會出現一些可識別的填充(padding)記錄。GFS客戶端追加失敗將重試,只要返回用戶追加成功,說明在所有副本中都至少追加成功了一次。當然,可能出現記錄在某些chunk副本中被追加了多次,即重復記錄;也可能出現一些可識別的填充記錄,應用程序需要能夠處理這些問題。

? ? ? ?另外,由于GFS支持多個客戶端并發追加,那么多個客戶端之間的順序是無法保證的,同一個客戶端連續追加成功的多個記錄也可能被打斷,比如客戶端先后追加成功記錄R1和R2,由于追加R1和R2這兩條記錄的過程不是原子的,中途可能被其它客戶端打斷,那么GFS的chunk中記錄的R1和R2可能不連續,中間夾雜著其它客戶端追加的數據。

? ? ? GFS的這種一致性模型是追求性能導致的,這也增加了應用程序開發的難度。對于那些padding、垃圾數據和重復數據,應用需要自己過濾掉。至于過濾方式,可以使用CheckSum標記垃圾數據、重復數據可以使用唯一id標識。對于MapReduce應用,由于其批處理特性,可以先將數據追加到一個臨時文件,在臨時文件中維護索引記錄每個追加成功的記錄的偏移,等到文件關閉時一次性將臨時文件改名為最終文件。

2.2.5 數據完整性

? ? ? ?每個chunkserver使用checksum來偵測腐化的存儲數據。一個GFS集群經常包含幾百臺服務器、幾千個磁盤,磁盤故障導致數據腐化或丟失是常有的事兒。我們能利用其他正常的chunk副本恢復腐化的數據,但是通過跨chunkserver對比副本之間的數據來偵測腐化是不切實際的。另外,各副本內的字節數據出現差異也是正常的、合法的(原子的record append就可能導致這種情況,不會保證完全一致的副本,但是不影響客戶端使用)。因此,每個chunkserver必須靠自己來核實數據完整性,其對策就是維護checksum。

? ? ? ?一個chunk被分解為多個64KB的塊。每個塊有對應32位的checksum。像其他元數據一樣,checksum被保存在內存中,并用利用日志持久化保存,與用戶數據是隔離的。

? ? ? ?在讀操作中,chunkserver會先核查讀取區域涉及的數據塊的checksum。因此chunkserver不會傳播腐化數據到客戶端(無論是用戶客戶端還是其他chunkserver)。如果一個塊不匹配checksum,chunkserver向請求者明確返回錯誤。請求者收到此錯誤后,將向其他副本重試讀請求,而master則會盡快從其他正常副本克隆數據創建新的chunk。當新克隆的副本準備就緒,master命令發生錯誤的chunkserver刪除異常副本。

? ? ? ?checksum對讀性能影響不大。因為大部分讀只會跨幾個塊,checksum的數據量不大。GFS客戶端代碼在讀操作中可以盡量避免跨越塊的邊界,進一步降低checksum的花費。而且chunkserver查找和對比checksum是不需要任何I/O的,checksum的計算通常也在I/O 等待時被完成,不爭搶CPU資源。

? ? ? ?checksum的計算是為append操作高度優化的,因為append是我們的主要應用場景。append時可能會修改最后的塊、也可能新增塊。對于修改的塊只需增量更新其checksum,對于新增塊不管它有沒有被填滿都可以計算其當前的checksum。對于最后修改的塊,即使它已經腐化了而且append時沒有檢測到,還對其checksum執行了增量更新,此塊的checksum匹配依然會失敗,在下次被讀取時即能偵測到。

? ? ? ?普通的寫操作則比append復雜,它會覆蓋重寫文件的某個區域,需要在寫之前檢查區域首尾塊的checksum。它不會創建新的塊,只會修改老的塊,而且不是增量更新。對于首尾之間的塊沒有關系,反正是被全量的覆蓋。而首尾塊可能只被覆蓋了一部分,又不能增量更新,只能重新計算整個塊的checksum,覆蓋老checksum,此時如果首尾塊已經腐化,就無法被識別了。所以必須先檢測后寫。

? ? ? ?在系統較空閑時,chunkserver會去掃描和檢查不太活躍的chunk。這樣那些很少被讀的chunk也能被偵測到。一旦腐化被偵測到,master會為其創建一個新副本,并刪除腐化副本。GFS必須保證每個chunk都有足夠的有效副本以防不可逆的丟失,chunk不活躍可能會導致GFS無法察覺它的副本異常,此機制可以有效的避免這個風險。

2.3 高性能架構

2.3.1 緩存Chunk元數據

? ? ? ?客戶端用文件名和 Chunk 索引作為 key 緩存這些信息。在對這個 Chunk 的后續讀取操作中,客戶端不必再和 Master 節點通訊了,除非緩存的元數據信息過期或者文件被重新打開。實際上,客戶端通常會在一次請求中查詢多個 Chunk 信息, Master 節點的回應也可能包含了緊跟著這些被請求的 Chunk 后面的 Chunk 的信息。在實際應用中,這些額外的信息在沒有任何代價的情況下,避免了客戶端和 Master 節點未來可能會發生的幾次通訊。

2.3.2 Chunk尺寸

? ? ? 選擇較大的 Chunk 尺寸有幾個重要的優點:

? ? ? + 減少了客戶端和 Master 節點通訊的需求,因為只需要一次和 Mater 節點的通信就可以獲取 Chunk 的位置信息,之后就可以對同一個 Chunk 進行多次的讀寫操作。

? ? ? + 采用較大的 Chunk 尺寸,客戶端能夠對一個塊進行多次操作,這樣就可以通過與 Chunk 服務器保持較長時間的 TCP 連接來減少網絡負載。

? ? ? + 選用較大的 Chunk 尺寸減少了 Master節點需要保存的元數據的數量,這就允許我們把元數據全部放在內存中。

? ? ? Chunk設大的壞處:

? ? ? ?+ 小文件可能只有一個chunk,有可能成為熱點chunk。

? ? ? ?+ 內部碎片

2.3.3 租約機制

? ? ? ?如前2.2.2所述,寫操作是在主Chunk的控制下執行的,這種租約機制使得 Master 節點的管理負擔最小化。

? ? ? ?GFS數據追加以記錄為單位,每個記錄的大小為幾十KB到幾MB,如果每次記錄追加都需要請求Master,那么Master顯然會成為系統的性能瓶頸,因此,GFS系統中通過Lease機制將chunk寫操作授權給Chunk Server。獲取Lease授權的Chunk Server稱為Primary Chunk Server,其它副本所在的Chunk Server稱為Secondary Chunk Server。Lease授權針對單個chunk,在Lease有效期內,對該chunk的寫操作都有Primary Chunk Server負責,從而減少Master的負擔。一般來說,Lease的有效期比較長,比如60秒,只要沒有出現異常,Primary Chunk Server可以不斷向Master請求延長Lease的有效期直到整個chunk寫滿。

? ? ? 假設有Chunk A在GFS中保存了三個副本A1,A2,A3,其中,A1是Primary。如果副本A2所在Chunk Server下線后又重新上線,并且在A2下線的過程中,副本A1和A3有新的更新,那么,A2需要被Master當成垃圾回收掉。GFS通過對每個chunk維護一個版本號來解決,每次給Chunk進行Lease授權或者Primary Chunk Server重新延長Lease有效期時,Master會將Chunk的版本號加1。A2下線的過程中,副本A1和A3有新的更新,說明Primary Chunk Server向Master重新申請Lease并增加了A1和A3的版本號,等到A2重新上線后,Master能夠發現A2的版本號太低,從而將A2標記為可刪除的chunk,Master的垃圾回收任務會定時檢查,并通知Chunk Server將A2回收掉。

2.3.4 數據流

? ? ? ?GFS寫流程有一個特色:流水線及分離數據流與控制流。流水線操作用來減少延時。當一個ChunkServer接收到一些數據,它就立即開始轉發。由于采用全雙工網絡,立即發送數據并不會降低接收數據的速率。拋開網絡阻塞,傳輸B個字節到R個副本的理想時間是B/T + RL,其中T是網絡吞吐量,L是亮點之間的延時。假設采用千兆網絡,L通常小于1ms,傳輸1MB數據到多個副本的時間小于80ms。分離數據流與控制流主要是為了優化數據傳輸,每一個機器都是把數據發送給網絡拓撲圖上”最近”的尚未收到數據的數據。舉個例子,假設有三臺ChunkServer S1,S2和S3,S1與S3在同一個機架上,S2在另外一個機架,客戶端部署在機器S1上。如果數據先從S1轉發到S2,再從S2轉發到S3,需要經歷兩次跨機架數據傳輸;相對地,按照GFS中的策略,數據先發送到S1,接著從S1轉發到S3,最后轉發到S2,只需要一次跨機架數據傳輸。

? ? ? ?分離數據流與控制流的前提是每次追加的數據都比較大,比如MapReduce批處理系統,而且這種分離增加了追加流程的復雜度。如果采用傳統的Primary/backup復制方法,追加流程會在一定程度上得到簡化。

數據流

? ? ? ?Client將待追加數據發送到Primary Chunk Server,Primary Chunk Server可能收到多個客戶端的并發追加請求,需要確定操作順序,并寫入本地;

? ? ? ?Primary將數據通過流水線的方式轉發給所有的Secondary;

? ? ? ?每個Secondary Chunk Server收到待追加的記錄數據后寫本地,所有副本都在本地寫成功并且收到后一個副本的應答消息時向前一個副本回應,比如上圖中A需要等待B應答成功且本地寫成功后才可以應答Primary。

? ? ? ?Primary應答客戶端。如果客戶端在超時時間之內沒有收到Primary的應答,說明發生了錯誤,需要重試。

? ? ? ?當然,實際的追加流程遠遠沒有這么簡單。追加的過程中可能出現Primary Lease過期而失去chunk修改操作的授權,Primary或者Secondary機器出現故障,等等。

2.3.5 快照

? ? ? ?快照(Snapshot)操作是對源文件/目錄進行一個”快照”操作,生成該時刻源文件/目錄的一個瞬間狀態存放與目標文件/目錄中。GFS中使用標準的copy-on-write機制生成快照,也就是說,”快照”只是增加GFS中chunk的引用計數,表示這個chunk被快照文件引用了,等到客戶端修改這個chunk時,才需要在Chunk Server中拷貝chunk的數據生成新的chunk,后續的修改操作落到新生成的chunk上。

? ? ? ?為了對某個文件做Snapshot,首先需要停止這個文件的寫服務,接著增加這個文件的所有chunk的引用計數,以后修改這些chunk時會拷貝生成新的chunk。對某個文件執行Snapshot的大致步驟如下:

? ? ? ?1)通過Lease機制收回對文件每一個chunk寫權限,停止對文件的寫服務;

? ? ? ?2)Master拷貝文件名等元數據生成一個新的Snapshot文件;

? ? ? ?3) 對執行Snapshot的文件的所有chunk增加引用計數;

? ? ? ?例如,對文件foo執行快照操作生成foo_backup,foo在GFS中有三個chunk C1,C2和C3。Master首先需要收回C1,C2和C3的寫Lease,從而保證文件foo處于一致的狀態,接著Master復制foo文件的元數據生成foo_backup,foo_backup同樣指向C1,C2和C3。快照前,C1,C2和C3只被一個文件foo引用,因此引用計數為1;執行快照操作后,這些chunk的引用計數增加為2。以后客戶端再次往C3追加數據時,Master發現C3的引用計數大于1,通知C3所在的Chunk Server本次拷貝C3生成C3′,客戶端的追加操作也相應地轉向C3′。

2.3.6 無阻塞創建Checkpoint

? ? ? ?由于創建一個 Checkpoint 文件需要一定的時間,所以 Master 服務器的內部狀態被組織為一種格式,這種格式要確保在 Checkpoint 過程中不會阻塞正在進行的修改操作。 Master 服務器使用獨立的線程切換到新的日志文件和創建新的 Checkpoint 文件。

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • 關于Mongodb的全面總結 MongoDB的內部構造《MongoDB The Definitive Guide》...
    中v中閱讀 32,010評論 2 89
  • 分布式文件系統的主要功能有兩個:一個是存儲文檔、圖像、視頻之類的Blob類型數據;另外一個是作為分布式表格系統的持...
    olostin閱讀 3,250評論 1 5
  • title: GFS 小結 tags: GFS 分布式 categories: paper 分布式 comment...
    zhaif閱讀 6,039評論 0 3
  • 引言 GFS是谷歌2003年提出的一個文件系統。雖然GFS比較古老,但是后來的HDFS,是受到了GFS的啟發,是G...
    炸茄盒閱讀 2,567評論 1 5
  • ??Google File System(簡稱GFS)是適用于大規模且可擴展的分布式文件系統,可以部署在廉價的商務...
    薛少佳閱讀 14,680評論 0 9