GFS 論文閱讀筆記

The Google File System

Abstract

GFS,一個可擴展的分佈式文件系統,用於大型分佈式數據密集型應用程序。它可以在廉價的通用硬件上運行時提供容錯功能,並且可以爲大量客戶端提供較高的聚合性能。

我們已經設計並實現了Google File System,這是一個可擴展的分佈式文件系統,用於大型分佈式數據密集型應用程序。它可以在廉價的通用硬件上運行時提供容錯功能,並且可以爲大量客戶端提供較高的聚合性能。
在實現與以前的分佈式文件系統相同的許多目標的同時,我們的設計是由對我們當前和預期的應用程序工作負載和技術環境的觀察推動的,這反映出與某些早期文件系統假設的明顯偏離。這使我們重新審視了傳統選擇,並探索了根本不同的設計要點。
GFS 已成功滿足我們的存儲需求。它已在Google內廣泛部署,作爲存儲平臺,用於生成和處理我們服務所使用的數據以及需要大量數據集的研發工作。迄今爲止,最大的羣集可在一千臺計算機上的數千個磁盤上提供數百TB的存儲量,並且數百個客戶端可以同時訪問它。
在本文中,我們介紹了旨在支持分佈式應用程序的文件系統接口擴展,討論了我們設計的許多方面,並報告了來自微基準測試和實際使用情況的測量結果。

keywords:Fault tolerance,scalability,data storage,clustered storage

1 Introduction

GFS 的目標:性能,可伸縮性,可靠性和可用性

GFS 和傳統的分佈式文件系統在設計上不同的點:

  • 組件故障是正常現象,而不是例外情況
  • 需要處理的文件容量都很大
  • 大多數文件是通過附加新數據而不是覆蓋現有數據來進行改變的 appending not overwriting
  • 共同設計應用程序和文件系統的API使得整個系統的靈活性更好

我們已經設計並實施了Google文件系統(GFS),以滿足Google數據處理需求快速增長的需求。 GFS與以前的分佈式文件系統具有許多相同的目標,例如性能,可伸縮性,可靠性和可用性。但是,其設計是由對我們當前和預期的應用程序工作負載和技術環境的主要觀察驅動的,這反映出與某些早期文件系統設計假設存在明顯差異。我們重新審視了傳統選擇,並探索了設計空間中的根本不同點。

首先,組件故障是正常現象,而不是例外情況。文件系統由數百個或什至數千個由廉價商品部件構建的存儲機組成,並由相當數量的客戶端計算機訪問。組件的數量和質量實際上保證了某些組件在任何給定的時間都無法運行,並且某些組件無法從當前的故障中恢復。我們已經看到了由應用程序錯誤,操作系統錯誤,人爲錯誤以及磁盤,內存,連接器,網絡和電源故障引起的問題。因此,持續監視,錯誤檢測,容錯和自動恢復必須是系統不可或缺的。

其次,按照傳統標準,文件很大,多GB文件是常見的。每個文件通常包含許多應用程序對象,例如Web文檔。當我們定期處理包含數十億個對象的許多TB的快速增長的數據集時,即使文件系統可以支持它,也無法管理數十億個大約KB大小的文件。結果,必須重新考慮設計假設和參數,例如I / O操作和塊大小。

第三,大多數文件是通過附加新數據而不是覆蓋現有數據來進行改變的。文件內的隨機寫入實際上是不存在的。寫入後,僅讀取文件,並且通常只能順序讀取。各種數據共享這些特徵。有些可能會構成大型存儲庫,數據分析程序會掃描這些存儲庫。有些可能是運行應用程序連續生成的數據流。有些可能是檔案數據。一臺機器上同時產生或稍後產生的中間結果可能是中間結果。鑑於對大型文件的這種訪問方式,附加成爲性能優化和原子性保證的重點,而在客戶端中緩存數據塊卻失去了吸引力。

第四,共同設計應用程序和文件系統API通過增加靈活性來使整個系統受益。 例如,我們放鬆了GFS的一致性模型,以極大地簡化文件系統,而不會給應用程序帶來繁重的負擔。 我們還引入了原子附加操作,以便多個客戶端可以併發附加到文件,而無需它們之間的額外同步。 這些將在本文後面詳細討論。

​ 當前部署了多個GFS羣集以用於不同的目的。 最大的服務器擁有1000多個存儲節點,超過300 TB的磁盤存儲,並且連續不斷地被數百臺不同計算機上的客戶端大量訪問。

2 Design Overview

2.1 Assumptions

在設計滿足我們需求的文件系統時,我們以既帶來挑戰又帶來機遇的假設爲指導。我們前面提到了一些關鍵的觀察,現在更詳細地闡述了我們的假設。

  • 該系統由許多經常發生故障的廉價組件組成。它必須不斷地自我監控,並定期檢測,容忍並從組件故障中迅速恢復。
  • 系統存儲少量的大文件。我們期望有幾百萬個文件,每個文件的大小通常爲100 MB或更大。多GB文件是常見情況,應進行有效管理。同時必須支持小文件,但我們不需要對其進行優化。
  • 工作負載主要包括兩種讀取:大型流讀取和小型隨機讀取。在大型流讀取中,單個操作通常讀取數百KB,更常見的是1 MB或更多。來自同一客戶端的連續操作通常會讀取文件的連續區域。少量隨機讀取通常以任意偏移量讀取幾個KB。注重性能的應用程序經常對小讀取進行批處理和排序,以穩定地通過文件而不是來回移動。
  • 工作中還具有許多大的順序寫入,這些寫入將數據追加到文件中。典型的操作大小類似於讀取的大小。寫入後,很少再次修改文件。支持對文件中任意位置的小寫操作,但不一定要有效。
  • 系統必須爲同時附加到同一文件的多個客戶端有效地實現定義良好的語義。我們的文件通常被用作生產者-消費者隊列或進行多路合併。每臺計算機上運行一個的數百個生產者將併發地附加到文件中。具有最小同步開銷的原子性是必不可少的。該文件可能會在以後讀取,或者消費者可能正在同時讀取文件。
  • 高持續帶寬比低延遲更重要。我們的大多數目標應用程序都以高速率處理大量數據,而很少有對單個讀取或寫入的嚴格響應時間要求。

2.2 Interface

GFS提供了一個熟悉的文件系統接口,具有創建,刪除,打開,關閉,讀取和寫入文件的常規操作,此外,GFS具有快照和記錄附加操作。

GFS提供了一個熟悉的文件系統接口,儘管它沒有實現諸如POSIX之類的標準API。 文件在目錄中按層次結構組織,並由路徑名標識。 我們支持創建,刪除,打開,關閉,讀取和寫入文件的常規操作。

此外,GFS具有快照和記錄附加操作。 快照可以低成本創建文件或目錄樹的副本。 記錄追加允許多個客戶端同時將數據追加到同一文件中,同時保證每個客戶端的追加的原子性。 這對於實現多路合併結果和生產者-消費者隊列非常有用,許多客戶可以同時附加這些隊列而無需附加鎖定。 我們發現這些類型的文件在構建大型分佈式應用程序中具有不可估量的價值。 快照和記錄追加將分別在3.4和3.3節中討論。

2.3 Architecture

image-20200627124937953

一個GFS集羣由一個 master和多個 chunkservers 組成,並且可以由多個 clients 訪問,如圖1所示。它們中的每一個通常都是運行用戶級服務器進程的商用Linux計算機。只要在機器資源允許的情況下,就可以在同一臺機器上同時運行chunkserver和 client,並且可以接受由於運行不穩定的應用程序代碼而導致的較低可靠性。

文件分爲固定大小的塊。每個塊都由塊創建時主機分配的不可變且全局唯一的64 bit chunk handle 來標識。chunk server將塊作爲Linux文件存儲在本地磁盤上,並讀取或寫入由塊句柄和字節範圍指定的塊數據。爲了提高可靠性,每個塊都複製到多個chunk server上。默認情況下,我們存儲三個副本,儘管用戶可以爲文件namespce的不同區域指定不同的複製級別。

Master維護所有文件系統元數據。這包括namespce,訪問控制信息,從文件到塊的映射以及塊的當前位置。它還控制整個系統範圍內的活動,例如塊租約管理,孤立塊的垃圾回收以及chunk server之間的塊遷移。master 週期性地與HeartBeat消息中的每個chunk server通信,以向其發出指令並收集其狀態

鏈接到每個應用程序的GFS客戶端代碼實現文件系統API,並與Master和chunk server通信以代表該應用程序讀取或寫入數據。客戶端與Master交互以進行元數據操作,但是所有承載數據的通信都直接傳遞給chunk server。我們不提供POSIX API,因此不需要掛接到Linux vnode層。

客戶端和chunk server均不緩存文件數據。客戶端緩存幾乎沒有好處,因爲大多數應用程序會流過大文件或工作集太大而無法緩存。沒有它們,就消除了緩存一致性問題,從而簡化了客戶端和整個系統。 (但是,客戶端確實緩存元數據。)chunk server不需要緩存文件數據,因爲大塊存儲爲本地文件,因此Linux的緩衝區緩存已經將經常訪問的數據保存在內存中。

2.4 Single Master

只有一個 Master 可以極大地簡化我們的設計,並使 Master 可以使用全局數據來制定複雜的塊放置和複製決策。但是,我們必須最小化它在讀寫中的參與,以免它成爲瓶頸。客戶端永遠不會通過Master讀取和寫入文件數據。取而代之的是,客戶端詢問Master應該聯繫哪些chunkserver。它在有限的時間內緩存此信息,並直接與chunkserver交互以進行許多後續操作。

讓我們參考圖1來解釋簡單閱讀的交互。首先,客戶端使用固定的塊大小,將應用程序指定的文件名和字節偏移量轉換爲文件內的塊索引。然後,它向主機發送一個包含文件名和塊索引的請求。Master 答覆相應的塊句柄和副本的位置。客戶端使用文件名和塊索引作爲關鍵字來緩存此信息。

然後,客戶端將請求發送到其中一個副本,很可能是最接近的副本。該請求指定了塊句柄和該塊內的字節範圍。在緩存信息過期或重新打開文件之前,對同一塊的進一步讀取不再需要客戶端與主機之間的交互。實際上,客戶端通常會在同一請求中請求多個塊,而Master也可以在請求的塊之後立即包含塊的信息。這些額外的信息可以繞開未來的幾項客戶與主控之間的交互,而幾乎無需有額外的代價。

2.5 Chunk Size

塊大小是關鍵設計參數之一。

我們選擇了64 MB,它比典型的文件系統塊大得多。每個塊副本都作爲純Linux文件存儲在塊服務器上,並且僅在需要時進行擴展。惰性空間分配避免了由於內部碎片而浪費空間,這可能是對如此大的塊大小最被人詬病的地方。

大塊塊具有幾個重要的優點。

  • 首先,它減少了客戶與主機交互的需求,因爲對同一塊的讀寫只需要向主機發出一個初始請求即可獲得塊位置信息。減少交互請求對於我們的工作量尤其重要,因爲應用程序通常按順序讀取和寫入大文件。即使對於小的隨機讀取,客戶端也可以輕鬆地緩存多TB工作集的所有塊位置信息。
  • 其次,由於在較大的塊上,客戶端更有可能在給定的塊上執行許多操作,因此它可以通過在延長的時間段內保持與塊服務器的持久TCP連接來減少網絡開銷
  • 。第三,它減小了存儲在Master上的元數據的大小。這使我們能夠將元數據保留在內存中,這又帶來了其他優勢,我們將在2.6.1節中討論。

缺點:

  • 一個小文件由少量塊組成,也許只有一個。如果許多客戶端正在訪問同一文件,則存儲這些塊的塊服務器可能會成爲熱點。

另一方面,即使有惰性空間分配,較大的塊大小也有其缺點。一個小文件由少量塊組成,也許只有一個。如果許多客戶端正在訪問同一文件,則存儲這些塊的塊服務器可能會成爲熱點。實際上,熱點並不是主要問題,因爲我們的應用程序通常會順序讀取大型的多塊文件。

但是,當批處理隊列系統首次使用GFS時,熱點確實出現了:將可執行文件作爲單塊文件寫入GFS,然後同時在數百臺機器上啓動。數以百計的同時請求使存儲此可執行文件的少數塊服務器過載。我們通過存儲具有更高複製因子的可執行文件並通過使批處理隊列系統錯開應用程序的啓動時間來解決此問題。潛在的長期解決方案是在這種情況下允許客戶端從其他客戶端讀取數據。

2.6 Metadata

Master 存儲三種類型的元數據:

  • 文件和 chunk 的命名空間
  • 文件到 chunks 的映射
  • 每個 chunk 的冗餘所在的位置

所有的元數據都存放在 Master 的內存中。

前兩種類型(names-paces和files-chunk的映射)也通過將改變 log 到存儲在主機本地磁盤上並複製到遠程計算機上的操作日誌中而保持不變。 使用日誌可以使我們簡單,可靠地更新主狀態,而不會在主崩潰時冒不一致的風險。

Master不會持久存儲 chunkserver 中 chunk 的信息。 相反,它會在主啓動時以及每當一個塊服務器加入集羣時就向每個塊服務器詢問其塊。

2.6.1 In-Memory Data Structures

由於元數據存儲在內存中,因此 Master 操作速度很快。

這種僅使用內存的方法的一個潛在問題是,塊的數量以及整個系統的容量受到主機擁有多少內存的限制

如果需要支持甚至更大的文件系統,則向Master添加額外內存的成本也很小,以補償我們通過將元數據存儲在內存中而獲得的簡單性,可靠性,性能和靈活性。

由於元數據存儲在內存中,因此 Master 操作速度很快。此外,對於 Master 而言,在後臺定期掃描其整個狀態既簡單又有效。這種定期掃描用於實現大塊垃圾收集,在大塊服務器故障時進行重新複製以及大塊遷移以平衡大塊服務器之間的負載和磁盤空間使用情況。第4.3節和第4.4節將進一步討論這些活動。

這種僅使用內存的方法的一個潛在問題是,塊的數量以及整個系統的容量受到主機擁有多少內存的限制。在實踐中,這不是嚴重的限制。每個64 MB塊的主數據維護少於64個字節。大多數塊已滿,因爲大多數文件包含許多塊,只有最後一部分可能會被部分填充。同樣,文件namespce數據每個文件通常需要少於64個字節,因爲它使用前綴壓縮來緊湊地存儲文件名。

如果需要支持甚至更大的文件系統,則向Master添加額外內存的成本也很小,以補償我們通過將元數據存儲在內存中而獲得的簡單性,可靠性,性能和靈活性。

2.6.2 Chunk Locations

Master 不保留有關哪個塊服務器具有給定塊副本的持久記錄。

它只是在啓動時輪詢chunkserver以獲取該信息。之後,Master可以保持最新狀態,因爲它可以控制所有塊的放置並通過常規HeartBeat消息監視塊服務器的狀態。

我們最初嘗試將塊位置信息永久保留在Master上,但是我們決定,在啓動時以及隨後定期從塊服務器請求數據要簡單得多。這消除了在塊服務器加入和離開集羣,更改名稱,失敗,重新啓動等時,使Master和塊服務器保持同步的問題。在具有數百臺服務器的羣集中,這些事件經常發生。

理解該設計決策的另一種方法是認識到,chunk server 對自己的磁盤上有或沒有的塊有最終的決定權。試圖在 Master 上維護此信息的一致性視圖是沒有意義的,因爲塊服務器上的錯誤可能導致塊自發消失(例如,磁盤可能變質並被禁用),或者操作員可能會重命名塊服務器。

2.6.3 Operation Log

操作日誌包含關鍵元數據更改的歷史記錄。它是GFS的核心。

它不僅是元數據的唯一持久記錄,而且還用作定義併發操作順序的邏輯時間表。文件和塊及其版本(請參見第4.5節)均由創建它們的邏輯時間唯一地標識。

由於操作日誌至關重要,因此我們必須可靠地存儲日誌。在元數據改變永久性修改完成之前,客戶端對這些改變不可見。

Master通過覆蓋操作日誌來恢復其文件系統狀態。

日誌的內部結構的構造方式可以在不延遲新的日誌改變的的情況下創建新的檢查點(新線程)。

恢復僅需要最新的完整檢查點和後續日誌文件。

操作日誌包含關鍵元數據更改的歷史記錄。它是GFS的核心。它不僅是元數據的唯一持久記錄,而且還用作定義併發操作順序的邏輯時間表。文件和塊及其版本(請參見第4.5節)均由創建它們的邏輯時間唯一唯一地標識。

由於操作日誌至關重要,因此我們必須可靠地存儲日誌,在元數據改變永久性修改完成之前,客戶端對這些改變不可見。否則,即使這些數據塊本身仍然存在,我們也會有效地丟失整個文件系統或最近的客戶端操作。因此,我們將其複製到多臺遠程計算機上,並且僅在將相應的日誌記錄刷新到本地和遠程磁盤後才響應客戶端操作。Master在刷新之前將一批日誌記錄一起批處理,從而減少了刷新和複製對整個系統吞吐量的影響。

Master通過覆蓋操作日誌來恢復其文件系統狀態。爲了最大程度地縮短啓動時間,我們必須使日誌較小。每當日誌增長到超過特定大小時,主控點都會檢查其狀態,以便可以通過從本地磁盤加載最新的檢查點並在此之後僅覆蓋有限數量的日誌記錄來進行恢復。該檢查點採用類似於B樹的緊湊形式,可以直接映射到內存中並用於namespce查找,而無需進行額外解析。這進一步加快了恢復速度並提高了可用性。

由於建立檢查點可能要花費一些時間,因此,內部結構的構造方式可以在不延遲新的到來改變的的情況下創建新的檢查點。Master切換到新的日誌文件,並在單獨的線程中創建新的檢查點。新的檢查點包括切換之前的所有變化。對於具有數百萬個文件的羣集,可以在一分鐘左右的時間內創建它。完成後,將其本地和遠程寫入磁盤。

恢復僅需要最新的完整檢查點和後續日誌文件。可以自由刪除較舊的檢查點和日誌文件,儘管我們保留了一些檢查點以防災難。檢查點期間的故障不會影響正確性,因爲恢復代碼會檢測並跳過不完整的檢查點。

2.7 Consistency Model

GFS擁有一個寬鬆的一致性模型,該模型可以很好地支持我們高度分散的應用程序,但實現起來相對簡單有效。 現在,我們討論GFS的guarantee及其對應用的意義。 我們還將重點介紹GFS如何維護這些保證,但將細節留給本文的其他部分。

2.7.1 Guarantees by GFS

GFS應用程序可以使用其他目的已經需要的一些簡單技術來適應寬鬆的一致性模型:

  • 依靠append而不是overwrite
  • 檢查點 checkpoint
  • 編寫自我驗證,自我識別的記錄 self-validating and self-identifying records

文件namespce變化(例如文件創建)是原子的。它們僅由Master處理:namespce鎖定確保原子性和正確性(第4.1節);主操作日誌定義了這些操作的全局總順序(第2.6.3節)。

數據變化後文件區域的狀態取決於變化的類型,無論這次數據變化它是成功還是失敗以及是否存在併發變化。表1總結了這些情況結果。如果所有客戶端始終讀取相同的數據,則無論它們從哪個副本讀取文件,文件區域都是一致的。

如果文件數據變化是一致的,則這個 region 稱爲被 defined,客戶端將看到該變化的全部內容。當變化成功而不受併發編寫者的干擾時,受影響的區域被稱爲 defined(並暗示一致):所有客戶端將始終看到該變化所寫的內容。

併發成功的變化使該區域不確定但保持一致:所有客戶都看到相同的數據,但可能無法反映任何一個變化所寫的內容。通常,它由來自多個變化的混合片段組成。失敗的變化會使區域不一致(因此也稱爲 undefined):不同的客戶端可能在不同的時間看到不同的數據。我們在下面描述應用程序如何區分已定義區域和未定義區域。應用程序不需要進一步區分不同種類的未定義區域。

image-20200627133539354

數據變化可以是寫或記錄追加。寫操作導致以應用程序指定的文件偏移量寫入數據。記錄附加會導致數據(“record”)至少在原子上附加一次,即使存在併發變化,但也會偏離GFS的選擇(第3.3節)。 (相反,“常規”追加只是在客戶機認爲是文件當前結尾的偏移量處進行寫操作。)該偏移量將返回給客戶機,並標記包含記錄的已定義區域的開始。此外,GFS可能會在其間插入填充或記錄重複項。它們佔據被認爲不一致的區域,通常被用戶的數據減少了。

經過一系列成功的變化後,可以確保定義了變化的文件區域,幷包含最後一次變化寫入的數據十位。 GFS通過(a)在其所有副本上以相同順序對塊應用變化來實現此目的(第3.1節),以及(b)使用塊版本號來檢測任何已過時的副本,因爲它丟失了變化而又過時了chunkserver關閉(第4.5節)。陳舊的副本永遠不會參與任何變化,也不會提供給客戶向 master 詢問塊位置。儘早把它們當作垃圾收集它們。

由於客戶端緩存塊的位置,因此它們可以在刷新該信息之前從陳舊的副本中讀取。該窗口受到緩存條目的超時和文件的下一次打開的限制,文件的下一次打開會從緩存中清除該文件的所有塊信息。此外,由於我們的大多數文件都是僅追加文件,所以陳舊的副本通常會返回塊的結尾的之前的地方,而不是過時的數據。當讀者重試並聯系master時,它將立即獲得當前塊的位置。

成功進行變化後很長時間,組件故障當然仍會破壞或破壞數據。 GFS通過Master和所有塊服務器之間的定期握手來識別故障的塊服務器,並通過校驗和檢測數據損壞(第5.2節)。一旦出現問題,將盡快從有效副本中恢復數據(第4.3節)。只有在GFS可以做出反應之前(通常在數分鐘之內),所有塊的所有副本都丟失,纔可以不可逆地丟失塊。即使在這種情況下,它也變得不可用,沒有損壞:應用程序收到明顯的錯誤,而不是損壞的數據。

2.7.2 Implications for Applications

實際上,我們所有的應用程序都通過追加而不是覆蓋來使文件發生變化。

在一種典型的用法中,編寫者會從頭到尾生成一個文件。寫入所有數據後,它會自動將文件原子重命名爲永久性名稱,或定期檢查成功寫入了多少數據。檢查點還可以包括應用程序級別的校驗和。讀取器僅驗證和處理直到最後一個檢查點的文件區域,已知該檢查點處於已定義狀態。無論一致性和併發性問題如何,這種方法都爲我們提供了很好的服務。與隨機寫操作相比,追加操作對應用程序故障的處理效率更高,並且更具彈性。檢查點允許編寫者以增量方式重新啓動,並阻止讀者處理從應用程序角度來看仍不完整的成功寫入文件數據。

在另一種典型用法中,許多作者同時將文件追加到文件中以合併結果或作爲生產者-消費者隊列。記錄追加的至少一次追加語義會保留每個作者的輸出。讀者對偶爾的填充和重複內容的處理如下。作者編寫的每條記錄都包含諸如校驗和之類的額外信息,以便可以驗證其有效性。讀者可以使用校驗和識別並丟棄多餘的填充並記錄片段。如果它不能容忍偶爾的重複(例如,如果它們會觸發非冪等的操作),則可以使用記錄中的唯一標識符將其過濾掉,而無論如何要爲相應的應用程序實體(例如Web文檔)命名,通常都需要使用這些標識符。記錄I / O的這些功能(重複刪除除外)在我們的應用程序共享的庫代碼中,並且適用於Google的其他文件接口實現。這樣一來,相同的記錄順序,加上稀有的重複項,始終會傳送到記錄讀取器。

3 System Interactions

我們設計了該系統,以最大程度地減少 Master 在所有操作中的參與。在此背景下,我們現在描述 client,Master 和 chunk servers 如何交互以實現數據變化,原子記錄追加和快照。

3.1 Leases and Mutation Order

變化是一種更改塊的內容或元數據的操作,例如寫操作或追加操作。 每個變化都在所有區塊的副本中執行。 我們使用租約來維護副本之間一致的變化順序。 主服務器將塊租約授予其中一個副本,我們稱之爲主副本。 主數據庫爲該塊的所有變化選擇一個序列順序。 應用變化時,所有副本均遵循此順序。 因此,全局變化順序首先由主機選擇的租約授權順序定義,而在租約範圍內則由主服務器分配的序列號定義。

租約機制旨在最大程度地減少主服務器上的管理開銷。 租約的初始超時爲60秒。 但是,只要對塊進行了變化,主節點就可以無限期地請求主節點並通常從主節點接收擴展。 這些擴展請求和授權會承載在主機與所有塊服務器之間定期交換的HeartBeat消息上。 主機有時可能會嘗試在租約到期之前撤銷租約(例如,當主機希望禁用正在重命名的文件上的變化時)。 即使主服務器與主服務器失去通信,它也可以在舊租約到期後安全地將新租約授予另一個副本。

在圖2中,我們通過遵循這些編號步驟的寫入控制流程來說明此過程:

image-20200627174723219

  1. 客戶端詢問主服務器,哪個塊服務器持有該塊的當前租約以及其他副本的位置。 如果沒有人租用,則主服務器將其授予其選擇的副本(未向外表現)。
  2. Master 答覆 primary 副本的身份以及其他(輔助)副本的位置。 客戶端緩存此數據以備將來使用。 僅當主服務器無法訪問或答覆它不再擁有租約時,它才需要再次與主服務器聯繫。
  3. 客戶端將數據推送到所有副本。一個客戶可以以任何順序進行。每個塊服務器將數據存儲在內部LRU緩衝區高速緩存中,直到使用或老化數據爲止。通過將數據流與控制流分離,我們可以通過基於網絡拓撲調度昂貴的數據流來提高性能,而不管哪個塊服務器是主要的。 3.2節將對此進行進一步討論。
  4. 一旦所有副本均確認已接收到數據,客戶端將向主數據庫發送寫請求。該請求標識了更早推送到所有副本的數據。主數據庫爲可能從多個客戶端接收到的所有變化分配連續的序列號,這提供了必要的序列化。它將變化按序列號順序應用於其自身的本地狀態。
  5. 主服務器將寫請求轉發到所有輔助副本。每個輔助副本均按照主副本分配的相同序列號順序應用變化。
  6. 輔助節點均答覆主要節點,表明它們已完成操作。
  7. primary 節點答覆客戶。在任何副本上遇到的任何錯誤都將報告給客戶端。萬一出現錯誤,寫操作可能會在輔助副本的主副本和任意子集成功完成。 (如果它在主服務器上失敗,則不會分配序列號並轉發。)客戶端請求被視爲失敗,並且修改後的區域處於不一致狀態。我們的客戶代碼通過重試失敗的變化來處理此類錯誤。在從寫入開始返回到重試之前,它將在步驟(3)到(7)進行一些嘗試。

如果應用程序的寫操作較大或跨越塊邊界,則GFS客戶端代碼會將其分解爲多個寫操作

它們都遵循上述控制流程,但可能與其他客戶端的併發操作交錯並被其覆蓋。 因此,共享文件區域可能最終包含來自不同客戶端的片段,儘管這些副本將是相同的,因爲各個操作在所有副本上均以相同的順序成功完成。 如第2.7節所述,這會使文件區域處於一致但未定義的狀態。

3.2 Data Flow

我們將數據流與控制流分離開來,以有效地使用網絡。

當控制權從客戶端流向主服務器,然後流至所有第二服務器時,數據將以流水線方式沿着精心挑選的塊服務器鏈線性推送。我們的目標是充分利用每臺計算機的網絡帶寬,避免網絡瓶頸和高延遲鏈接,並最小化推送所有數據的延遲。

爲了充分利用每臺機器的網絡帶寬,數據會沿着區塊服務器鏈線性推送,而不是以其他拓撲結構(例如,樹)進行分發。

因此,每臺機器的全部出站帶寬用於儘可能快地傳輸數據,而不是在多個接收者之間分配。

爲了儘可能避免網絡瓶頸和高延遲鏈接(例如,交換機間鏈接經常同時出現),每臺計算機都將數據轉發到網絡拓撲中尚未接收到數據的“最近”計算機。

假設客戶端將數據推送到塊服務器S1到S4。它將數據發送到最近的塊服務器,例如S1。 S1通過最接近S1的S4(例如S2)將其轉發到最接近的塊服務器S2。同樣,S2將其轉發到S3或S4,以更接近S2的那個爲準,依此類推。我們的網絡拓撲非常簡單,可以從IP地址準確估計“距離”。

最後,我們通過流水線化TCP連接上的數據傳輸來最大程度地減少延遲。

一旦塊服務器接收到一些數據,它將立即開始轉發。流水線對我們特別有用,因爲我們使用具有全雙工鏈接的交換網絡。立即發送數據不會降低接收率。在沒有網絡擁塞的情況下,將B字節傳輸到R副本的理想經過時間是B / T + RL,其中T是網絡吞吐量,L是在兩臺機器之間傳輸字節的延遲。我們的網絡鏈路通常爲100 Mbps(T),L遠遠低於1 ms。因此,理想情況下,可以在大約80毫秒內分配1 MB。

3.3 Atomic Record Appends

GFS提供了一個稱爲記錄追加的原子追加操作。

在傳統的寫入中,客戶端指定要寫入數據的偏移量。併發寫入同一區域不可序列化:該區域最終可能包含來自多個客戶端的數據片段。但是,在記錄追加中,客戶端僅指定數據。 GFS會以GFS選擇的偏移量將它至少一次原子(即,作爲一個連續的字節序列)附加到文件中一次,並將該偏移量返回給客戶端。這類似於在多個編寫器同時執行的情況下,寫入以O_APPEND模式在Unix中打開的文件而沒有競爭條件。

記錄追加在我們的分佈式應用程序中被大量使用,其中不同計算機上的許多客戶端同時向同一文件追加。

如果客戶使用傳統寫入操作,則它們將需要其他複雜且昂貴的同步,例如通過分佈式鎖管理器。在我們的工作負載中,此類文件通常充當多生產者/單消費者隊列,或包含來自許多不同客戶端的合併結果。

記錄追加是一種變化,它遵循第3.1節中的控制流,而在主記錄中僅包含一些額外的邏輯。

客戶端將數據推送到文件最後一個塊的所有副本,然後,將其請求發送到主服務器。主數據庫檢查是否將記錄追加到當前塊上會導致該塊超過最大大小(64 MB)。如果是這樣,它會將數據塊填充到最大大小,告訴輔助數據庫執行相同的操作,然後回覆客戶端以指示應在下一個數據塊上重試該操作。 (記錄追加被限制爲最大塊大小的四分之一,以使最壞情況的碎片保持在可接受的水平。)如果記錄適合最大大小(通常是這種情況),則主記錄將數據追加到它的副本,告訴輔助服務器將數據寫入數據所處的確切偏移位置,最後將成功回覆給客戶端。

如果記錄追加在任何副本上均失敗,則客戶端將重試該操作。

結果,同一塊的副本可能包含不同的數據,可能全部或部分包含同一記錄的副本。 GFS不保證所有副本在字節上都是相同的。它僅保證將數據作爲原子單位至少寫入一次。從簡單的觀察很容易得出此屬性,即爲了報告操作成功,必須在某個塊的所有副本上以相同的偏移量寫入數據。此外,在此之後,所有副本的長度至少與記錄的結尾一樣長,因此,即使以後其他副本成爲主要副本,任何將來的記錄也將被分配更高的偏移量或其他塊。就我們的一致性保證而言,成功記錄追加操作已在其中寫入其數據的區域是定義的(因此是一致的),而中間區域是不一致的(因此是未定義)。正如我們在2.7.2節中討論的那樣,我們的應用程序可以處理不一致的區域。

3.4 Snapshot

快照操作幾乎可以瞬間複製文件或目錄樹(“源”),同時最大程度地減少正在進行的變化的中斷。我們的用戶使用它來快速創建大型數據集的分支副本(通常是遞歸創建這些副本的副本),或者在嘗試進行以後可以輕鬆提交或回滾的更改之前檢查當前狀態。

像AFS [5]一樣,我們使用標準的寫時複製技術來實現快照。

當主服務器接收到快照請求時,它首先撤銷要快照的文件中的塊上所有未完成的租約。這確保了對這些塊的任何後續寫入都將需要與主機進行交互以找到租約持有人。這將使Master 有機會首先創建塊的新副本。

租約被撤銷或過期後,Master 將操作記錄到磁盤。然後,它通過複製源文件或目錄樹的元數據,將此日誌記錄應用於其內存中狀態。新創建的快照文件指向與源文件相同的塊。

快照操作之後,客戶端第一次要寫入塊C時,它將向主機發送請求以查找當前的租約持有人。主機注意到塊C的引用計數大於1。它推遲了對客戶請求的答覆,而是選擇了一個新的塊句柄C’。然後,它要求具有當前C副本的每個塊服務器創建一個名爲C’的新塊。通過在與原始服務器相同的chunkserver上創建新的chunk,我們確保可以在本地複製數據,而不是通過網絡複製(我們的磁盤速度大約是我們100 Mb,是以太網鏈路速度的三倍)。從這一點來看,請求處理與任何塊都沒有什麼不同:主服務器授予副本之一對新塊C'的租約,並回復客戶端,客戶端可以正常寫入該塊,而不知道它剛被從現有塊創建。

4 Master Operation

主機的工作:

  • 命名空間管理和鎖定
  • 副本放置決策
  • 創建副本,再複製副本,再平衡副本
  • 回收未使用的存儲
  • 陳舊副本檢測

4.1 Namespace Management and Locking

許多主操作可能需要很長時間:例如,快照操作必須撤銷快照覆蓋的所有塊上的塊服務器租約。我們不想在其他主操作運行時延遲它們。

因此,我們允許多個操作處於活動狀態,並在名稱空間的區域上使用鎖以確保正確的序列化。

與許多傳統文件系統不同,GFS沒有按目錄的數據結構來列出該目錄中的所有文件。它也不支持相同文件或目錄的別名(即,Unix術語中的硬鏈接或符號鏈接)。 GFS在邏輯上將其命名空間表示爲將完整路徑名映射到元數據的查找表。通過前綴壓縮,可以在內存中有效地表示該表。命名空間樹中的每個節點(絕對文件名或絕對目錄名)都有一個關聯的讀寫鎖。

GFS在邏輯上將其命名空間表示爲將完整路徑名映射到元數據的查找表。通過前綴壓縮,可以在內存中有效地表示該表。命名空間樹中的每個節點(絕對文件名或絕對目錄名)都有一個關聯的讀寫鎖。

每個主操作在運行之前都需要獲取一組鎖。

通常,如果涉及到/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 Replica Placement

一個GFS羣集高度分佈在一個以上的層次上。它通常具有分佈在許多機架上的數百個塊服務器。反過來,可以從相同或不同機架的數百個客戶端訪問這些塊服務器。不同機架上的兩臺計算機之間的通信可能會通過一個或多個網絡交換機。另外,進出機架的帶寬可能小於機架內所有計算機的總帶寬。多級分發對分發數據以實現可伸縮性,可靠性和可用性提出了獨特的挑戰。

塊副本放置策略有兩個目的:

  • 最大化數據可靠性和可用性
  • 最大化網絡帶寬利用率

對於兩者而言,僅在計算機之間散佈副本是不夠的,僅防止磁盤或計算機故障並充分利用每臺計算機的網絡帶寬。

我們還必須將大塊副本散佈在機架上。這樣可確保即使整個機架損壞或脫機(例如,由於共享資源(如網絡交換機或電源電路)故障),大塊的某些副本仍可生存並保持可用。這也意味着一個塊的流量(尤其是讀取)可以利用多個機架的聚集帶寬。另一方面,寫入流量必須流經多個機架,這是我們樂意做出的權衡。

4.3 Creation,Re-replication,Rebalancing

創建塊副本的原因有以下三個:

  • 塊創建

    當 Master 創建一個塊時,它選擇放置最初爲空的副本的位置。它考慮了幾個因素。

    (1)我們要在磁盤空間利用率低於平均水平的磁盤服務器上放置新副本。隨着時間的推移,這將使塊服務器之間的磁盤利用率均等。

    (2)我們想限制每個塊服務器上“最近”創建的數量。儘管創建本身很便宜,但它可以可靠地預測即將到來的大量寫流量,因爲大塊是在需要寫時創建的,並且在我們的“一次讀取多次”工作負載中,它們通常在被寫入後實際上變成只讀的。完全書面。

    (3)如上所述,我們希望在機架上分佈塊的副本。

  • 重新複製

一旦可用副本的數量低於用戶指定的目標,主服務器就會重新複製塊。

可能由於多種原因而發生這種情況:

  1. chunkserver變得不可用,chunkserver報告其副本可能已損壞,由於錯誤而禁用了其中一個磁盤,或者提高了複製目標。需要重新複製的每個塊都基於多個因素確定優先級。一是離複製目標還有多遠。例如,我們給丟失了兩個副本的塊賦予了比僅丟失一個副本的塊更高的優先級。
  2. 另外,相對於屬於最近刪除的文件的塊,我們更喜歡先爲活動文件重新複製塊(請參見第4.4節)。
  3. 最後,爲了最大程度地減少故障對正在運行的應用程序的影響,我們提高了阻止客戶端進度的任何塊的優先級。

主機選擇最高優先級的塊,並通過指示某些塊服務器直接從現有有效副本中複製塊數據來對其進行“克隆”。

放置新副本的目標類似於創建副本的目標:均衡磁盤空間利用率,限制任何單個塊服務器上的活動克隆操作,以及將副本分佈在機架上。爲了防止克隆的通信量淹沒客戶端的通信量,主服務器限制羣集和每個塊服務器的活動克隆操作數。此外,每個塊服務器通過限制其對源塊服務器的讀取請求來限制其在每個克隆操作上花費的帶寬量。

  • 重新平衡

主服務器會定期重新平衡副本:它檢查當前副本分發,並移動副本以獲得更好的磁盤空間和負載平衡。同樣通過此過程,主服務器逐漸填充新的chunkserver,而不是立即用新的chunk和隨之而來的大量寫入流量將其淹沒。新副本的放置標準與上面討論的相似。此外,主服務器還必須選擇要刪除的現有副本。通常,它更喜歡刪除塊服務器上具有低於平均可用空間的磁盤,以平衡磁盤空間使用。

4.4 Garbage Collection

刪除文件後,GFS不會立即回收可用的物理存儲。它僅在文件和塊級別的常規垃圾回收期間懶惰地執行此操作。我們發現這種方法使系統更加簡單和可靠。

4.4.1 Mechanism

當應用程序刪除文件時,主機會像其他更改一樣立即記錄該刪除。但是,不是立即回收資源,而是將文件重命名爲包含刪除時間戳的隱藏名稱。

在主文件系統定期掃描文件系統名稱空間的過程中,如果這些隱藏文件已存在超過三天(時間間隔是可配置的),它將刪除這些文件。在此之前,仍可以使用新的特殊名稱讀取文件,並且可以通過將其重命名爲正常名稱來取消刪除該文件。當從名稱空間中刪除隱藏文件時,其內存中的元數據將被刪除。這有效地切斷了其所有塊的鏈接。

在對塊名稱空間的類似常規掃描中,主服務器識別孤立的塊(即,無法從任何文件訪問的塊)並清除這些塊的元數據。在定期與主服務器交換的HeartBeat消息中,每個塊服務器報告其擁有的塊的子集,而主服務器將答覆不再存在於主服務器的元數據中的所有塊的標識。塊服務器可以自由刪除其此類塊的副本。

4.4.2 Discussion

儘管分佈式垃圾回收是一個困難的問題,需要在編程語言的上下文中提供複雜的解決方案,但在我們的案例中,這非常簡單。我們可以輕鬆地識別所有對塊的引用:

它們在主機專有的文件到塊的映射中。我們還可以輕鬆識別所有塊副本:它們是每個塊服務器上指定目錄下的Linux文件。主機不知道的任何此類副本都是“垃圾”。

與立即刪除相比,用於回收存儲的垃圾收集方法具有多個優點。

  • 首先,它在組件故障常見的大型分佈式系統中既簡單又可靠。塊創建可能會在某些塊服務器上成功,但在其他一些塊服務器上不會成功,從而留下主服務器不知道的副本。副本刪除消息可能會丟失,主服務器必須記住要在失敗時重新發送它們,無論是自身的還是塊服務器的。垃圾收集提供了一種統一且可靠的方式來清理所有未知有用的副本。
  • 其次,它將存儲回收活動合併到主數據庫的常規後臺活動中,例如,對名稱-速度和與塊服務器握手的常規掃描。因此,它是分批完成的,成本也要攤銷。而且,僅在主服務器相對空閒時才執行此操作。管理員可以更迅速地響應需要及時關注的客戶請求。
  • 第三,回收存儲的延遲爲防止意外的,不可逆的刪除提供了一個安全網。

根據我們的經驗,主要缺點是延遲刪除有時會妨礙用戶在存儲空間時進行微調使用

重複創建和刪除臨時文件的應用程序可能無法立即重用存儲。如果刪除的文件再次被明確刪除,我們將通過加快存儲相關性來解決這些問題。我們還允許用戶將不同的複製和回收策略應用於名稱空間的不同部分。例如,用戶可以指定某個目錄樹中文件中的所有塊都應存儲而不復制,並且所有已刪除的文件都會立即且不可撤消地從文件系統狀態中刪除。

4.5 Stale Replica Detection

如果塊服務器發生故障並且在其關閉時丟失對塊的變化,則塊副本可能會過時。對於每個塊,Master 維護一個塊版本號,以區分最新副本和陳舊副本。

每當主服務器授予塊上的新租約時,它都會增加塊版本號並通知最新的副本。

主服務器和這些副本都在其持久狀態下記錄新版本號。這發生在任何客戶端被通知之前,因此在它可以開始寫入塊之前。如果另一個副本當前不可用,則其塊版本號不會提高。當塊服務器重新啓動並報告其塊組及其關聯的版本號時,主服務器將檢測到該塊服務器具有陳舊的副本。如果主服務器看到的版本號大於其記錄中的版本號,則主設備會認爲在授予租約時它失敗了,因此將更高的版本更新爲最新版本。

主機在其常規垃圾回收中刪除陳舊的副本。在此之前,它在答覆客戶端對塊信息的請求時實際上認爲陳舊的副本根本不存在。作爲另一種保護措施,當主機在克隆操作中通知客戶端哪個chunkserver持有該chunk的租約時或當它指示chunkserver從另一個chunkserver讀取chunk時,master會包含chunk版本號。客戶端或塊服務器在執行操作時會驗證版本號,以便它始終在訪問最新數據。

5 Fault Tolerance And Diagnosis

在設計系統時,我們面臨的最大挑戰之一就是:

應對頻繁出現的組件故障

組件的質量和數量使這些問題比異常更爲常見:我們不能完全信任機器,也不能完全信任磁盤。 組件故障可能導致系統不可用,或者更糟的是損壞數據。 我們討論瞭如何應對這些挑戰,以及我們在系統中內置的工具,用於在不可避免的情況下診斷問題。

5.1 High Availability

在GFS羣集中的數百臺服務器中,某些服務器在任何給定時間都將不可用。我們通過兩個簡單而有效的策略使整個系統保持高可用性:快速恢復和複製

5.1.1 Fast Recovery

無論主服務器和塊服務器如何終止,他們均能夠在恢復其狀態並在幾秒鐘內啓動。 實際上,我們不區分正常終止和異常終止。 通常,通過殺死進程來關閉服務器。 客戶端和其他服務器因未完成的請求超時,重新連接到重新啓動的服務器並重試時,會稍有一點停頓的時間。 第6.2.2節報告了觀察到的啓動時間。

5.1.2 Chunk Replication

如前所述,每個塊都在不同機架上的多個塊服務器上覆制。用戶可以爲文件名稱空間的不同部分指定不同的複製級別。默認值爲三。主服務器根據需要克隆現有的副本,以在塊服務器脫機或通過校驗和驗證檢測損壞的副本時保持每個塊完全複製(請參閱第5.2節)。

儘管複製爲我們提供了很好的服務,但我們仍在探索其他形式的跨服務器冗餘,例如奇偶校驗或擦除代碼,以滿足日益增長的只讀存儲需求。我們期望在我們非常松耦合的系統中實施這些更復雜的冗餘方案具有挑戰性,但很容易管理,因爲我們的流量主要由追加和讀取操作而不是較小的隨機寫入操作控制

5.1.3 Master Replication

複製 Master 的狀態是爲了提高可靠性。它的操作日誌和檢查點被複制到多臺計算機上。僅在將其狀態記錄的日誌記錄刷新到本地磁盤和所有主副本上的磁盤之後,才認爲狀態變化已提交。

爲簡單起見,一個主進程負責所有變化以及後臺活動(例如在內部更改系統的垃圾回收)。當它失敗時,它幾乎可以立即重新啓動。如果其計算機或磁盤發生故障,則GFS外部的監視基礎結構將使用複製的操作日誌在其他位置啓動新的主進程。客戶端僅使用主機的規範名稱(例如 gfs-test ),這是一個DNS別名,如果將主機重定位到另一臺計算機,則可以更改該DNS別名。

而且,“影子”master即使在主master宕機時也提供對文件系統的只讀訪問。

它們是影子主機,而不是鏡像主機,因爲它們可能會稍微滯後於初級,通常是幾分之一秒。它們提高了未被主動變化的文件或不介意獲得稍微陳舊結果的應用程序的讀取可用性。實際上,由於從塊服務器讀取文件內容,因此應用程序不會觀察到過時的文件內容。短窗口內可能過時的是文件元數據,例如目錄內容或訪問控制信息。

爲了使自己隨時瞭解情況,影子主機讀取了不斷增長的操作日誌的副本,並對其數據結構進行了與主數據庫完全相同的更改序列。像 primary 一樣,它在啓動時(且之後很少)輪詢塊服務器,以查找塊副本,並與它們交換頻繁的握手消息以監視其狀態。它僅依賴於主服務器上的副本位置更新,該更新是由主服務器決定創建和刪除副本而導致的。

5.2 Data Integrity

每個塊服務器使用校驗和來檢測存儲數據的損壞。

鑑於GFS羣集通常在數百臺計算機上擁有數千個磁盤,因此它經常會遇到磁盤故障,從而導致數據損壞或讀寫路徑丟失。 (一個原因請參見第7節。)我們可以使用其他塊副本從損壞中恢復,但是通過比較塊服務器之間的副本來檢測損壞是不切實際的。而且,不同的複製品可能是合法的:GFS變化的語義,尤其是如前所述的原子記錄附加,不能保證相同的複製品。因此,每個塊服務器必須通過維護校驗和來獨立驗證其副本的完整性。

一塊被分成64 KB的塊。每個都有對應的32位校驗和。像其他元數據一樣,校驗和與用戶數據分開保存在內存中,並通過日誌記錄持久存儲。

對於讀取,塊服務器在將任何數據返回給請求者(無論是客戶端還是其他塊服務器)之前,會驗證與讀取範圍重疊的數據塊的校驗和。因此,塊服務器將不會將損壞傳播到其他計算機。

如果塊與記錄的校驗和不匹配,則塊服務器將錯誤返回給請求者,並將不匹配項報告給主服務器。作爲響應,請求者將從其他副本中讀取數據,而主服務器將從其他副本中克隆該塊。放置有效的新副本後,主服務器指示報告不匹配的塊服務器刪除其副本。

由於以下幾個原因,校驗和對讀取性能的影響很小。

  • 由於我們的大多數讀取操作至少跨越幾個塊,因此我們僅需要讀取和校驗和相對少量的額外數據即可進行驗證。 GFS客戶端代碼通過嘗試在校驗和塊邊界對齊讀取來進一步減少了這種開銷。
  • 此外,無需任何I / O即可完成對塊服務器的校驗和查找和比較,並且校驗和計算通常會與I / O重疊。

由於校驗和計算在我們的工作負載中占主導地位,因此校驗和計算針對附加到塊末尾的寫入(與覆蓋現有數據的寫入相反)進行了優化。我們只是增量地更新最後的部分校驗和塊的校驗和,併爲附加的填充的任何全新校驗和塊計算新的校驗和。

即使最後一個部分校驗和塊已經損壞,而我們現在也無法檢測到,新的校驗和值將與存儲的數據不匹配,並且下次讀取該塊時,將像往常一樣檢測到損壞。

相反,如果寫入覆蓋了塊的現有範圍,則必須讀取並驗證要覆蓋範圍的第一個和最後一個塊,然後執行寫入操作,最後計算並記錄新的校驗和。如果我們在部分覆蓋第一個和最後一個塊之前沒有對其進行驗證,則新的校驗和可能會隱藏未覆蓋區域中存在的損壞。

在空閒期間,塊服務器可以掃描並驗證非活動塊的內容。這使我們能夠檢測很少讀取的塊中的損壞。一旦檢測到損壞,主服務器即可創建新的未損壞副本,並刪除損壞的副本。這樣可以防止不活動但已損壞的塊副本欺騙主服務器,使其認爲它具有足夠的有效塊副本。

5.3 Diagnostic Tools

廣泛而詳細的診斷日誌記錄在問題隔離,調試和性能分析方面提供了不可估量的幫助,同時僅產生了最小的成本。如果沒有日誌,則很難理解機器之間的瞬時,不可重複的交互。 GFS服務器生成診斷日誌,該日誌記錄了許多重要事件(例如,上下啓動的塊服務器)以及所有RPC請求和答覆。這些診斷日誌可以自由刪除,而不會影響系統的正確性。但是,我們嘗試將這些日誌保留在空間允許的範圍內。

RPC日誌包括在線上發送的確切請求和響應,但讀取或寫入的文件數據除外。通過將請求與答覆進行匹配並在不同的計算機上整理RPC記錄,我們可以重建整個交互歷史以診斷問題。日誌還可用作負載測試和性能分析的跟蹤。

日誌記錄對性能的影響很小(遠遠超過了好處),因爲這些日誌是順序和異步寫入的。最新事件也保存在內存中,可用於連續在線監視。

6 Measurements

在本節中,我們介紹一些基準測試,以闡明GFS架構和實施中固有的瓶頸,以及Google使用的實際集羣中的一些數字。

6.1 Micro-benchmarks

我們在由一個主服務器,兩個主副本,16個塊服務器和16個客戶端組成的GFS羣集上測量了性能。 請注意,此配置是爲了便於測試而設置的。 典型的集羣具有數百個塊服務器和數百個客戶端。

所有機器都配置有雙1.4 GHz PIII處理器,2 GB內存,兩個80 GB 5400 rpm磁盤以及與HP 2524交換機的100 Mbps全雙工以太網連接。 所有19臺GFS服務器計算機都連接到一臺交換機,所有16臺客戶機計算機都連接到另一臺。 兩個交換機通過1 Gbps鏈路連接。

6.1.1 Reads

N個客戶端同時從文件系統讀取。每個客戶端從320 GB的文件集中讀取隨機選擇的4 MB區域。重複256次,以便每個客戶端最終讀取1 GB數據。合在一起的塊服務器僅具有32 GB的內存,因此我們期望Linux緩衝區高速緩存中的命中率最高爲10%。我們的結果應該接近冷緩存結果。

圖3(a)顯示了N個客戶端的總讀取速率及其理論極限。當兩個交換機之間的1 Gbps鏈路達到飽和時,限制的峯值總計爲125 MB / s;如果其客戶端的100 Mbps網絡接口達到飽和,則每個客戶端爲12.5 MB / s(以適用者爲準)。當僅一個客戶端正在讀取時,觀察到的讀取速率爲10 MB / s,即每個客戶端限制的80%。對於16個讀取器,總讀取速率達到94 MB / s,約爲125 MB / s鏈接限制的75%,或每個客戶端6 MB / s。效率從80%下降到75%,因爲隨着讀取器數量的增加,多個讀取器同時從同一塊服務器讀取數據的可能性也隨之增加。

image-20200627193839727

6.1.2 Writes

N個客戶端同時寫入N個不同的文件。每個客戶端以1 MB的寫入順序將1 GB的數據寫入新文件。總寫入速率及其理論極限如圖3(b)所示。極限平穩期爲67 MB / s,因爲我們需要將每個字節寫入16個塊服務器中的3個,每個塊服務器具有12.5 MB / s的輸入連接。

一個客戶端的寫入速率爲6.3 MB / s,約爲限制的一半。造成這種情況的主要原因是我們的網絡堆棧。它與我們用於將數據推送到大塊副本的流水線方案互動性不是很好。將數據從一個副本傳播到另一個副本的延遲會降低總體寫入速率。

16個客戶端的總寫入速率達到35 MB / s(每個客戶端2.2 MB / s),約爲理論上限的一半。與讀取的情況一樣,隨着客戶端數量的增加,多個客戶端併發寫入同一塊服務器的可能性更高。而且,與每個16個讀寫器相比,與16個讀寫器發生衝突的可能性更大,因爲每個寫入涉及三個不同的副本。

寫的速度比我們想要的慢。實際上,這並不是一個主要問題,因爲即使它增加了各個客戶端看到的延遲,也不會顯着影響系統交付給大量客戶端的聚合寫帶寬。

6.1.3 Record Appends

圖3(c)顯示了記錄追加性能。 N個客戶端同時附加到單個文件。 性能由存儲文件的最後一個塊的塊服務器的網絡帶寬限制,而與客戶端數量無關。 對於一個客戶端,它的開始速度爲6.0 MB / s,對於16個客戶端,它的速度降至4.8 MB / s,這主要是由於不同客戶端看到的網絡傳輸速率的擁塞和差異所致。

目前,我們的應用程序傾向於生成多個此類文件。 換句話說,N個客戶端同時附加到M個共享文件,其中N和M都在數十個或數百個中。 因此,在我們的實驗中,chunkserver網絡擁塞實際上並不是一個重要問題,因爲客戶端可以在編寫一個文件時取得進展,而另一文件的chunkserver卻很忙。

6.2 Real World Clusters

現在,我們檢查一下Google內部正在使用的兩個集羣,它們分別代表了其他幾個集羣。

數百名工程師定期將Cluster A用於研發。 一個典型的任務是由人類用戶發起的,並且運行時間長達幾個小時。 它讀取從幾MB到幾TB的數據,轉換或分析數據,然後將結果寫回羣集。

羣集B主要用於生產數據處理。 這些任務的持續時間更長,並且僅在偶爾的人工干預下就可以連續生成和處理多結核病數據集。 在這兩種情況下,單個“任務”都由許多計算機上的多個進程同時讀取和寫入許多文件組成。

6.2.1 Storage

如表中的前五個條目所示,兩個集羣都有數百個塊服務器,支持許多TB的磁盤空間,並且相當但不是完全滿。 “已用空間”包括所有塊副本。幾乎所有文件都被複制3次。因此,羣集分別存儲18 TB和52 TB的文件數據。

這兩個羣集具有相似數量的文件,儘管B具有更多的失效文件,即已刪除或替換爲新版本但尚未回收其存儲的文件。它還具有更多的塊,因爲其文件往往更大。

image-20200627194301440

6.2.2 Metadata

總計中的chunk servers 存儲數十GB的元數據,主要是64 KB用戶數據塊的校驗和。保留在塊服務器上的唯一其他元數據是第4.5節中討論的塊版本號

保留在主服務器上的元數據要小得多,只有幾十MB,或者平均每個文件約100個字節。這與我們的假設一致,即主機的內存大小實際上不會限制系統的容量。每個文件的元數據大多數是以前綴壓縮形式存儲的文件名。其他元數據包括文件所有權和權限,從文件到塊的映射以及每個塊的當前版本。另外,對於每個塊,我們存儲當前副本位置和用於實現寫時複製的參考計數。

每個單獨的服務器(塊服務器和主服務器)都只有50到100 MB的元數據。因此,恢復速度很快:服務器僅需幾秒鐘即可從磁盤讀取此元數據,服務器便可以回答查詢。但是,主機會在一段時間(通常爲30到60秒)內徘徊,直到它從所有塊服務器獲取塊位置信息爲止。

6.2.3 Read and Write Rates

表3列出了不同時間段的讀寫速率。 進行這些測量時,兩個羣集都已經運行了大約一週。 (羣集最近已重新啓動,以升級到新版本的GFS。)

自重啓以來,平均寫入速率小於30 MB / s。 當我們進行這些測量時,B處於寫入活動突發的中間,該活動產生大約100 MB / s的數據,由於寫入傳播到三個副本,因此產生了300 MB / s的網絡負載。

讀取速率遠高於寫入速率。 如我們所假設的,總的工作量包含的讀取次數多於寫入次數。 兩個集羣都處於大量讀活動中。 特別是,A在前一週一直保持580 MB / s的讀取速率。 它的網絡配置可以支持750 MB / s,因此它可以有效地利用其資源。 羣集B可以支持1300 MB / s的峯值讀取速率,但是其應用程序僅使用380 MB / s。

image-20200627194540529

6.2.4 Master Load

表3還顯示發送到主服務器的操作速率約爲每秒200到500個操作。主機可以輕鬆地跟上該速率,因此對於這些工作負載而言,這不是瓶頸。

在早期版本的GFS中,主服務器有時是某些工作負載的瓶頸。它花費了大部分時間依次瀏覽大型目錄(包含數十萬個文件)以查找特定文件。此後,我們更改了主數據結構,以允許通過名稱空間進行有效的二進制搜索。現在,它可以輕鬆地支持每秒數千個文件訪問。如有必要,我們可以通過在名稱空間數據結構之前放置名稱查找緩存來進一步加快速度。

6.2.5 Recovery Time

塊服務器發生故障後,某些塊將變得複製不足,必須將其克隆以恢復其複製級別。恢復所有此類塊所需的時間取決於資源量。在一個實驗中,我們殺死了羣集B中的單個塊服務器。該塊服務器有大約15,000個塊,其中包含600 GB的數據。爲了限制對正在運行的應用程序的影響併爲計劃決策提供餘地,我們的默認參數將該羣集限制爲91個併發克隆(佔塊服務器數量的40%),其中每個克隆操作最多允許消耗6.25 MB / s(50 Mbps)。所有塊均在23.2分鐘內恢復,有效複製速率爲440 MB / s。

在另一個實驗中,我們殺死了兩個帶有大約16,000個塊和660 GB數據的塊服務器。此雙重故障將266個塊減少爲只有一個副本。這266個塊以較高的優先級進行克隆,並在2分鐘內全部恢復到至少2倍的複製,從而使羣集處於可以忍受另一個塊服務器故障而不會丟失數據的狀態。

6.3 Workload Breakdown

在本節中,我們將詳細介紹兩個GFS羣集上的工作量細分,這些工作量與6.2節中的工作量相當但不相同。羣集X用於研發,而羣集Y用於生產數據處理。

6.3.1 Methodology and Caveats

這些結果僅包括客戶端發起的請求,因此它們反映了我們的應用程序爲整個文件系統生成的工作量。它們不包括執行客戶端請求的服務器間請求或內部後臺活動,例如轉發的寫入或重新平衡。

I / O操作的統計信息基於從GFS服務器記錄的實際RPC請求中啓發式重建的信息。例如,GFS客戶端代碼可能將讀取分爲多個RPC,以增加並行度,我們從中推斷出原始讀取。由於我們的訪問模式是高度程式化的,因此我們希望任何錯誤都可能來自噪聲。應用程序的顯式日誌記錄可能會提供稍微更準確的數據,但是從邏輯上講,重新編譯並重新啓動成千上萬個正在運行的客戶端是不可能的,而且從許多機器中收集結果很麻煩。

應該注意不要過度概括我們的工作量。由於Google完全控制GFS及其應用程序,因此這些應用程序傾向於針對GFS進行調整,相反,GFS是爲這些應用程序設計的。這樣的相互影響也可能在通用應用程序和文件系統之間存在,但在我們的案例中,這種影響可能會更加明顯。

6.3.2 Chunkserver Workload

表4顯示了按大小劃分的操作分佈。讀取大小顯示出雙峯分佈。小讀操作(不到64 KB)來自於搜索密集型客戶端,這些客戶端在大文件中查找小數據。大量讀取(超過512 KB)來自對整個文件的長時間順序讀取。

image-20200627195048332

在集羣Y中,大量讀取根本不返回任何數據。我們的應用程序,尤其是生產系統中的應用程序,經常使用文件作爲生產者-消費者隊列。當使用者讀取文件末尾時,生產者會同時將其附加到文件中。有時,當消費者超過生產者時,不會返回任何數據。羣集X較少顯示此信息,因爲它通常用於短期數據分析任務,而不是長期分佈式應用程序。

寫入大小也表現出雙峯分佈。較大的寫入(超過256 KB)通常是由於寫入器內有大量緩衝造成的。寫入器緩衝較少的數據,更頻繁地檢查點或進行同步,或者僅爲較小的寫入(小於64 KB)生成較少的數據。

至於記錄追加,羣集 Y 的大型記錄追加百分比比羣集 X 高得多,因爲使用羣集Y的生產系統針對GFS進行了更積極的調整。

表5列出了各種大小的操作中傳輸的數據總量。對於所有類型的操作,較大的操作(超過256 KB)通常佔傳輸的大多數字節。由於隨機查找工作負載,小讀(64 KB以下)確實會傳輸一小部分但很大一部分的讀數據。

image-20200627195313272

6.3.3 Appends versus Writes

記錄追加被大量使用,尤其是在我們的生產系統中。對於羣集X,寫入的記錄與記錄追加的比率按傳輸的字節爲108:1,按操作計數爲8:1。對於生產系統使用的羣集Y,比率分別爲3.7:1和2.5:1。而且,這些比率表明,對於這兩個羣集,記錄追加往往比寫入大。但是,對於集羣X,在測量期間記錄追加的總體使用率很低,因此結果可能會被一兩個應用程序以及特定的緩衝區大小選擇所歪曲。

不出所料,我們的數據變化工作量主要是附加而不是覆蓋。我們測量了主副本上覆蓋的數據量。這近似於客戶端故意覆蓋以前寫入的數據而不是附加新數據的情況。對於羣集X,重寫佔不足0.0001%的已更改字節和不足0.0003%的變化操作。對於羣集Y,比率均爲0.05%。儘管這是分鐘,但仍然比我們預期的高。事實證明,這些覆蓋大多數是由於錯誤或超時導致的客戶端重試。它們本身不是工作量的一部分,而是重試機制的結果。

6.3.4 Master Workload

表6顯示了按主請求類型分類的細分。大多數請求要求塊位置(FindLocation)進行讀取,並要求租約所有者信息(FindLeaseLocker)進行數據變化。

image-20200627195346713

集羣X和Y看到的刪除請求數量明顯不同,因爲集羣Y存儲的生產數據集會定期重新生成並替換爲較新的版本。這種差異中的某些差異進一步隱藏在Open請求中的差異中,因爲文件的舊版本可能會通過從頭開始進行寫操作而被隱式刪除(Unix開放術語中的模式“ w”)。

FindMatchingFiles是一個模式匹配請求,它支持“ ls”和類似的文件系統操作。與對主服務器的其他請求不同,它可能處理名稱空間的很大一部分,因此可能很昂貴。羣集Y經常看到它,因爲自動數據處理任務傾向於檢查文件系統的各個部分以瞭解全局應用程序狀態。相比之下,羣集X的應用程序受用戶更明確的控制,並且通常提前知道所有需要的文件的名稱。

7 Experiences

在構建和部署GFS的過程中,我們遇到了許多問題,有些是操作方面的,有些是技術方面的。

最初,GFS被認爲是我們生產系統的後端文件系統。隨着時間的流逝,用法逐漸演變爲包括研發任務。它最初幾乎不支持權限和配額之類的東西,但現在包括了基本形式。儘管生產系統受到嚴格的紀律和控制,但用戶有時卻沒有。需要更多的基礎架構來防止用戶相互干擾。
我們最大的問題是磁盤和Linux相關的。我們的許多磁盤都向Linux驅動程序聲稱它們支持多種IDE協議版本,但實際上僅對較新版本進行了可靠的響應。由於協議版本非常相似,因此這些驅動器大多數都可以工作,但是偶爾的不匹配會導致驅動器和內核對驅動器的狀態持不同意見。由於內核問題,這將以靜默方式破壞數據。這個問題促使我們使用校驗和來檢測數據損壞,同時我們修改了內核以處理這些協議不匹配。

早些時候,由於fsync()的成本,我們在Linux 2.2內核上遇到了一些問題。它的成本與文件的大小成正比,而不與修改部分的大小成正比。對於我們的大型操作日誌而言,這是一個問題,尤其是在實施檢查點之前。我們使用同步寫入解決了一段時間,並最終遷移到Linux 2.4。

另一個Linux問題是單個讀寫器鎖,當它從磁盤分頁時(地址鎖)或修改mmap()調用中的地址空間(寫鎖),地址空間中的任何線程都必須持有該線程。在輕負載下,我們看到了系統中的瞬時超時,並努力尋找資源瓶頸或偶發的硬件故障。最終,我們發現,當磁盤線程分頁先前映射的數據時,該單個鎖阻止了主網絡線程將新數據映射到內存中。由於我們主要受網絡接口的限制,而不是受內存複製帶寬的限制,因此,我們通過使用pread()替換mmap()來解決此問題,但需要額外的副本。

儘管偶爾出現問題,Linux代碼的可用性也一次又一次地幫助我們探索和理解系統行爲。在適當的時候,我們改進內核並與開源社區共享所做的更改。

8 Related Work

9 Conclusions

GFS 展示了在商用硬件上支持大規模數據處理工作負載所必需的質量。儘管某些設計決策是特定於我們獨特設置的,但許多決策可能適用於規模和成本意識相似的數據處理任務。

我們首先根據當前和預期的應用程序工作負載和技術環境,重新檢查傳統的文件系統假設。我們的發現導致了設計領域的根本不同。我們將組件故障視爲正常現象,而不是例外情況,針對大型文件進行優化,這些文件通常會附加到(也許同時進行),然後再讀取(通常是順序執行),並且都擴展和放鬆了標準文件系統接口,以改善整個系統。

我們的系統通過不斷監控,複製關鍵數據以及快速自動恢復功能來提供容錯能力。塊複製使我們能夠容忍chunkserver故障。這些故障的發生率激發了一種新穎的在線修復機制,該機制定期透明地修復損壞並儘快補償丟失的複製品。此外,我們使用校驗和來檢測磁盤或IDE子系統級別的數據損壞,鑑於系統中的磁盤數量,這種情況變得非常普遍。

我們的設計爲許多同時執行各種任務的讀取器和寫入器提供了高總吞吐量。我們通過將通過主機的文件系統控制與直接在塊服務器和客戶端之間傳遞的數據傳輸分開來實現此目的。大型塊的大小和塊的租用將主操作對常見操作的影響降到最低,這將權限授予數據突變中的主副本。這樣就可以成爲一個簡單的,集中的主服務器,而不會成爲瓶頸。我們認爲,網絡堆棧的改進將解除當前對單個客戶端看到的寫入吞吐量的限制。

GFS已成功滿足了我們的存儲需求,並已在Google內部廣泛用作研究,開發和生產數據處理的存儲平臺。它是使我們能夠繼續創新並解決整個Web規模問題的重要工具。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章