Google File System學習筆記

本文是學習《大規模分佈式存儲系統》中Google File System部分整理的筆記,其中也參考了Google File System原理這篇博客中的內容。

1. Design Motivation

Google對現有的系統運行狀態以及應用系統進行總結,抽象出對文件系統的需求,主要有以下幾個方面:
1. 普通商用的機器硬件發生故障是常態;
2. 存儲的問題普遍比較大,幾個G的文件很常見;
3. 大部分的文件操作都是在追加數據,覆蓋原來寫入的數據的情況比較少見,隨機寫幾乎不存在;
4. 讀操作主要包括兩種,large streaming read和small random read;
5. 爲了應用使用方便,多客戶端並行地追加同一個文件需要非常高效;
6. 帶寬的重要性大於時延,目標應用是高速讀大塊數據的應用,對響應時間沒有過多的需求;

2. 系統架構

這裏寫圖片描述
GFS系統的節點可分爲三種角色:GFS master(主控服務器),GFS chunkserver(CS,數據塊服務器),GFS client。

1. master

主控服務器中維護系統的元數據,包括文件及chunk命名空間、文件到chunk之間的映射、chunk位置信息。它也負責整個系統的全局控制,如chunk租約管理、垃圾回收無用chunk、chunk複製等。主控服務器會定期與chunkserver通過心跳的方式交換信息。

2. chunkserver

GFS文件被劃分爲固定大小的數據塊(chunk),由主服務器在創建時分配一個64位全局唯一的chunk handle來標識。CS以普通Linux文件的形式將chunk存儲在磁盤中,讀或寫的時候需要指定文件名和字節範圍,然後定位到對應的chunk。爲了保證數據的可靠性,一個chunk一般會在多臺chunkserver上存儲,默認爲3份,但用戶也可以根據自己的需要修改這個值。

3. client

client是GFS應用端使用的API接口,client與master交互來獲取元數據信息,但是所有和數據相關的信息都是直接和chunkserver來交互的。
GFS中的客戶端不緩存文件數據,只緩存主控服務器中獲取的元數據,這是由GFS的應用特點決定的。GFS最主要的應用有兩個:MapReduce與Bigtable。對於MapReduce來說,GFS客戶端使用方式爲順序讀寫,沒有緩存文件數據的必要;Bigtable作爲分佈式表格系統,內部實現了一套緩存機制。

3. Master設計

3.1 Namespace Management and Locking

每個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的鎖產生衝突,/home/user/foo創建會被禁止。
這種加鎖機制的好處是對於同一個目錄下,可以並行的操作文件,例如,同一個目錄下並行的創建文件。

3.2 Chunk creation

GFS在創建chunk的時候,選擇chunkserver時需要考慮以下因素:

  • 磁盤空間使用率低於平均值的chunkserver;
  • 限制每臺chunkserver的最近的創建chunk的次數,因爲創建 chunk往往意味着後續需要寫大量數據,所以,應該把寫流量儘量均攤到每臺chunkserver上;
  • chunk的副本放在處於不同機架的chunkserver上。

3.3 Chunk Re-replication

當一個chunk的副本數量少於預設定的數量時,需要做複製的操作,例如,chunkserver宕機,副本數據出錯,磁盤損壞,或者設定的副本數量增加。
chunk的複製的優先級是按照下面的因素來確定的:
1. 丟失兩個副本的chunk比丟失一個副本的chunk的複製認爲優先級高;
2. 文件正在使用比文件已被刪除的chunk的優先級高;
3. 阻塞了client進程的chunk的優先級高。

chunk複製的時候,選擇新chunkserver要考慮的點:
1. 磁盤使用率;
2. 單個chunkserver的複製個數限制;
3. 多個副本需要在多個機架;
4. 集羣的複製個數限制;
5. 限制每個chunkserver的複製網絡帶寬,通過限制讀流量的速率來限制。

3.4 Rebalancing

週期性地檢查副本分佈情況,然後調整到更好的磁盤使用情況和負載均衡。master對於新加入的chunkserver會逐漸地遷移副本到上面,防止新chunkserver帶寬佔滿。

3.5 Master容錯

Master容錯與傳統方法類似,通過操作日誌加checkpoint的方式進行,並且有一臺稱爲“shadow master”的實時熱備。
Master的修改操作總是先記錄操作日誌,然後修改內存。爲了減少Master故障重啓後恢復時間,Master定期將內存中的數據以checkpoint文件的形式轉儲到磁盤中,從而減少回放的日誌量。GFS還執行實時熱備,所有的元數據修改操作必須保證發送到實時熱備纔算成功。遠程的實時熱備實時接收Master發送的操作日誌並在內存中回放操作。Master出現故障可以秒級切換到實時熱備提供服務。爲保證同一時刻只有一個Master,GFS採用Chubby進行選主操作。

3.6 垃圾回收

GFS採用延遲刪除的機制,當刪除文件後,GFS並不要求立即歸還可用的物理存儲,而是在元數據中將文件名改爲一個隱藏的名字,並且包含一個刪除時間戳。master定期檢查,如果發現文件刪除時間超過一段時間(比如3天),會把文件從內存元數據刪除,以後Chunkserver和master的心跳消息中,Chunkserver會報告自己的chunk集合,master會回覆在master元數據中已經不存在的chunk信息,Chunkserver會釋放這些chunk副本。
採用這種方式刪除的好處:

  • 利用心跳方式交互,在一次刪除失敗後,還可以通過下次心跳繼續重試操作;
  • 刪除操作和其他的全局掃描metadata的操作可以放到一起做。 壞處:
  • 有可能有的應用需要頻繁的創建和刪除文件,這種延期刪除方式會導致磁盤使用率偏高,GFS提供的解決方案是,對一個文件調用刪除操作兩次,GFS會馬上做物理刪除操作,釋放空間。

3.7 過期副本的處理

當一臺chunkserver掛掉的時候,有新的寫入操作到chunk副本,會導致chunkserver的數據不是最新的。
假設chunk A有三個副本A1、A2、A3,假如A2下線了又重新上線,在A2下線過程中A1和A3有更新,A2需要被master當做垃圾回收:GFS對每個chunk維護一個版本號,每次primary Chunkserver重新延長租約有效期時,master會將chunk的版本號加1。A2重新上線後,master發現A2的版本號低,將A2標記爲可刪除,master的垃圾回收任務會定時檢查,通知Chunkserver將A2回收。
master會把落後的chunk當做垃圾來清理,並且不會把落後的chunkserver的位置信息傳給client。

3.8 快照

快照是對源文件或者目錄進行一個“快照”,生成該時刻源文件或目錄的瞬間狀態。GFS對快照使用了寫時複製策略,快照只是增加GFS中chunk的引用計數,在客戶端修改這個chunk時,纔在Chunkserver中拷貝chunk數據生成新的chunk,後續的操作落到新的chunk上。
對文件執行快照大致步驟:
通過租約機制收回對文件的每個chunk寫權限,停止對文件的寫服務;
master拷貝文件名等元數據生成一個新的快照文件;
對執行快照的文件的所有chunk增加引用計數。
foo文件生成快照foo.bak,foo在GFS中有三個chunk:c1、c2、c3,Master首先回收c1、c2和c3的寫租約,Master複製foo文件的元數據用於生成foo.bak,boo.bak同樣指向c1、c2、c3。快照前,c1、c2和c3只被一個foo引用,引用計數爲1;快照後,引用計數爲2。客戶端向c3追加數據時,Master發現c3的引用計數大於1,通知c3所在的Chunkserver拷貝c3生成c3’,客戶端的追加操作都作用在c3’上。

4. Metadata

master上保存了三種元數據:
1. 命名空間,也就是整個文件系統的目錄結構以及chunk基本信息;
2. 文件到chunk之間的映射;
3. chunk副本的位置信息。
master需要持久化命名空間和文件到chunk之間的映射。使用內存存儲metadata的好處是讀取metadata速度快,方便master做一些全局掃描metadata相關信息的操作,比如負載均衡。
chunk副本的位置信息不需要持久化,因爲chunkserver維護這些信息,而且chunkserver容易宕機重啓,在出現故障時,master都需要修改內存和已經持久化存儲的位置信息。master故障重啓時通過chunkserver彙報可以獲取chunk副本的位置信息。

4.1 Operation Log

操作日誌的作用是:

  • 持久化存儲metadata;
  • 它的存儲順序定義了client並行操作最終的操作順序。

操作日誌會存儲在master和多臺遠程機器上,只有操作日誌在master和多塔遠程機器都寫入成功後,master纔會向client返回操作成功。爲了減少操作日誌在多臺機器同步對系統吞吐量的影響,可以將一批操作日誌形成一個請求,然後再寫入到master和其他遠程機器上。

當操作日誌達到一定大小時,master會做checkpoint,相當於把內存中的b-tree給dump到磁盤中。master重啓時,可以讀取最近一次的checkpoint,然後重放在checkpoint之後的操作日誌,這樣就大大減少了系統恢復時間。
在做checkpoint的時候,master會先切換到新的操作日誌上,然後運行新的線程做checkpoint,所以對於新來的請求是基本不會受到影響的。

5. Consistency Model

這裏寫圖片描述
GFS中consistent、defined的定義如下:
consistent:所有的客戶端都能看到一樣的數據,不管它們從哪個副本讀取;
defined:當一個文件區域發生操作後,client可以看到剛剛操作的所有數據,那麼說這次操作是defined。

下面分析表格中出現的幾種情況。
Write(Serial Success),單個寫操作,並且返回成功,那麼所有副本都寫入了這次操作的數據,因此所有客戶端都能看到這次寫入的數據,所以是defined。
Write(Concurrent Successes),多個寫操作,並且返回成功,由於多個客戶端寫請求發送給primary後,由primary來決定寫的操作順序,但是,有可能多個寫操作可能是有區域重疊的,這樣,最終寫完成的數據可能是多個寫操作數據疊加在一起,所以這種情況是consistent和undefined。
Write(Failure),寫操作失敗,則可能有的副本寫入了數據,有的沒有,所以是inconsistent。
Record Append(Serial Success and Concurrent Success),由於Record Append可能包含重複數據,因此,是inconsistent,由於整個寫入的數據都能看到,所以是defined。
Record Append(Failure),可能部分副本append成功,部分副本append失敗,所以,結果是inconsistent。

GFS用version來標記一個chunkserver掛掉的期間,是否有client進行了write或者append操作。每進行一次write或者append,version會增加。
需要考慮的點是client會緩存chunk的位置信息,有可能其中某些chunkserver已經掛掉又起來了,這個時候chunkserver的數據可能是老的數據,讀到的數據是會不一致的。讀流程中,好像沒有看到要帶version信息來讀的。這個論文中沒看到避免的措施,目前還沒有結果。

應用層需要採用的機制:用append而不是write,做checkpoint,writing self-validating和self-identifying records。具體地,如下:
1. 應用的使用流程是append一個文件,到最終寫完後,重命名文件
2. 對文件做checkpoint,這樣應用只需要關注上次checkpoint時的文件區域到最新文件區域的數據是否是consistent的,如果這期間發生不一致,可以重新做這些操作。
3. 對於並行做append的操作,可能會出現重複的數據,GFS client提供去重的功能。

6. 容錯和診斷

6.1 High Availability

爲了實現高可用性,GFS在通過兩方面來解決,一是fast recovery,二是replication。
Fast Recovery:
master和chunkserver都被設計成能夠在秒級重啓。
Chunk Replications:
每個chunk在多個機架上有副本,副本數量由用戶來指定。當chunkserver不可用時,GFS master會自動的複製副本,保證副本數量和用戶指定的一致。
Master Replication:
master的operation log和checkpoint都會複製到多臺機器上,要保證這些機器的寫都成功了,才認爲是成功。只有一臺master在來做garbage collection等後臺操作。當master掛掉後,它能在很多時間內重啓;當master所在的機器掛掉後,監控會在其他具有operation log的機器上重啓啓動master。
新啓動的master只提供讀服務,因爲可能在掛掉的一瞬間,有些日誌記錄到primary master上,而沒有記錄到secondary master上(這裏GFS沒有具體說同步的流程)。

6.2 Data Integrity

每個chunkserver都會通過checksum來驗證數據是否損壞的。
每個chunk被分成多個64KB的block,每個block有32位的checksum,checksum在內存中和磁盤的log中都有記錄。
對於讀請求,chunkserver會檢查讀操作所涉及block的所有checksum值是否正確,如果有一個block的checksum不對,那麼會報錯給client和master。client這時會從其他副本讀數據,而master會clone一個新副本,當新副本clone好後,master會刪除掉這個checksum出錯的副本。

7. 關鍵問題

7.1 Single Master和租約機制

GFS架構中只有單個master,這種架構的好處是設計和實現簡單,例如,實現負載均衡時可以利用master上存儲的全局的信息來做決策。但是,在這種架構下,要避免的一個問題是,應用讀和寫請求時,要弱化master的參與度,防止它成爲整個系統架構中的瓶頸。
其實在GFS中,master的內存不會成爲GFS瓶頸。chunk的元信息包括全局唯一ID、版本號、副本所在的Chunkserver編號、引用計數等,不超過64B。對於1PB的數據,總共需要的元信息:1PB*3/64MB*64=3GB。

GFS數據追加以記錄爲單位,每個記錄大小几十KB到幾MB不等,如果每一次記錄追加都需要請求master,那麼master會成爲系統的瓶頸。GFS系統中通過租約機制將chunk寫操作授權給chunkserver。擁有租約授權的chunkserver爲主chunkserver,其他爲備chunkserver。租約授權針對單個chunk,在租約有效期內,對該chunk的寫操作都由主chunkserver負責,從而減輕Master的負載。只要沒有出現異常,主chunkserver可以不斷的向Master請求延長租約有效期直到整個chunk寫滿。(這裏的意思是將某個chunk的追加操作授權給了一個chunkserver,之後client對這個chunk的操作不需要master參與,直接通過chunkserver就可以了)。
從一個請求流程來討論這個問題。首先,上層的應用把文件名和偏移量信息傳遞給client,client把得到的信息轉成文件名和chunk index,併發送給master。master把chunk handle和chunk所在chunkserver位置信息返回給client,client會把這個信息緩存起來。在下次應用需要讀這個chunk的時候就不需要從master獲取chunk的位置信息了,從而減小了master的壓力。
此外,GFS支持在一個請求中同時讀取多個chunk的位置信息,這樣更進一步的減少了client和master之間的交互次數。

7.2 chunk的大小

chunk的默認大小爲64MB,比一般的文件系統要打。
優點:

  • 可以減少client和master的交互次數,chunk
    size比較大的時候,多次讀可能是一塊chunk的數據,這樣,可以減少client向 master請求chunk位置信息的次數;
  • 對於同一個chunk,client可以和chunkserver之間保持持久連接,提升讀的性能;
  • chunk size越大,chunk的metadata的總大小就越小,使得chunk相關的metadata可以存儲在GFS
    master的內存中。

缺點:

  • chunk size越大時,可能對部分文件來講只有1個chunk,那麼這個時候對該文件的讀寫就會落到一個GFS
    chunkserver上,成爲熱點。

7.3 寫流程

這裏寫圖片描述

  1. client向master請求chunk每個副本所在的Chunkserver,其中主Chunkserver持有修改租約。如果沒有Chunkserver持有租約說明這個chunk最近沒有寫操作,Master會發起一個任務,按照一定的策略將這個chunk的租約授權給其中一臺Chunkserver。
  2. Master返回客戶端各個副本所在Chunkserver位置信息,客戶端緩存這些信息以供以後使用。如果不出故障,客戶端以後讀寫該chunk都不需要再次請求Master。
  3. 客戶端將要追加的記錄發送到每一個副本,每一個Chunkserver會在內部的LRU結構中緩存這些數據。
  4. 當所有副本確認收到數據,客戶端發起一個寫請求控制命令給主副本。由於主副本可能會接收帶多個客戶端對同一個chunk的併發追加操作,主副本將確定這些操作的順序並寫入本地。
  5. 主副本把寫請求提交給所有的備副本。每一個備副本會根據主副本確定的順序執行寫操作。
  6. 備副本成功完成後應答主副本。
  7. 主副本應答客戶端,如果有副本寫入發生錯誤,將出現主副本寫成功但是某些被副本不成功的情況,客戶端將重試。

GFS寫流程有兩個特色:流水線以及分離數據流與控制流。
流水線操作用來減少延遲,爲了充分利用每臺機器的網絡帶寬,上面寫流程的第3步採用了流水線。當一個chunkserver收到一些數據,它立即開始轉發。假設一臺機器總共有三個副本S1-S3,整個的流程爲:
- client選擇離它最近的chunkserver S1,開始推送數據;
- 當chunkserver S1收到數據後,它會立馬轉發到離它最近的chunkserver S2;
- chunkserver S2收到數據後,會立馬轉發給離它最近的chunkserver S3。
分離數據流與控制流爲了優化數據傳輸,是在第5步中應用的。
實際在寫和追加的過程中可能出現主副本租約過期而失去chunk修改操作的權限,以及Chunkserver出現故障等。

7.4 追加流程

追加的操作流程和寫差不多,主要有以下區別:
- client把數據推送到所有副本的最後一個chunk,然後發送寫請求到primary(這裏要這麼理解,GFS中的文件通常是遠大於64MB的,所以數據推送到副本的最後一個chunk);
- 主chunkserver首先檢查最後一個chunk的剩餘空間是否可以滿足當前寫請求,如果可以,那麼執行寫流程,否則,它會把當前的chunk的剩餘空間pad起來,然後告訴其他的副本也這麼幹,最後告訴client這個chunk滿了,寫入下個chunk。

如果追加操作部分失敗如何處理?
例如,寫操作要追加到S1-S3,但是,僅僅是S1,S2成功了,S3失敗了,GFS client會重試操作,假如第二次成功了,那麼S1,S2寫了兩次,S3寫了一次,目前的理解是GFS會先把失敗的記錄進行padding對齊到primary的記錄,然後再繼續append。

7.5 GFS追加過程中副本出現故障如何處理

對於備副本故障,寫入的時候會失敗,然後primary會返回錯誤給client。按照一般的系統設計,client會重試一定次數,發現還是失敗,這時候client會把情況告訴給master,master可以檢測chunkserver的情況,然後把最新的chunkserver信息同步給client,client端再繼續重試。
對於主副本故障,寫入的時候會失敗,client端應該是超時了。client端會繼續重試一定次數,發現還是一直超時,那麼把情況告訴給master,gfs master發現primary掛掉,會重新grant lease到其他chunkserver,並把情況返回給client。

7.6 master如何實現高可用

  • namespace和文件到chunk信息會通過操作日誌的方式持久化存儲,操作日誌會同步到包括master在內的多臺機器上;
  • 對metadata的做checkpoint,保證重啓後replay消耗時間比較短,checkpoint可以直接映射到內存使用,不用解析;
  • 在primary master發生故障的時候,並且無法重啓時,會有外部監控將secondary
    master,並提供讀服務。secondary master也會監控chunkserver的狀態,然後把primary
    master的日誌replay到內存中。

7.7 新建chunk如何選擇chunkserver?如何避免新機器上線其他機器同時遷移?

選擇chunkserver根據以下幾點:

  • 磁盤空間使用率低於平均值的chunkserver;
  • 限制每臺chunkserver最近創建chunk的次數,因爲創建chunk往往意味着後續需要寫入大量數據,所以,應該把寫流量均攤到每臺chunkserver;
  • chunk的副本放置於不同機架的chunkserver上。

在集羣中加入新的機器之後,由於新機器的負載會較低,容易造成其他節點同時向新機器遷移數據的情況。通過限制單個chunkserver的clone操作的個數,以及clone使用的帶寬來限制,即從源chunkserver度數據的頻率做控制。

7.8 chunkserver的數據結構如何設計

chunkserver主要是存儲64KB block的checksum信息,需要由chunk+offset,能夠快速定位到checksum,可以用hashmap。

7.9 chunkserver如何應對磁盤可能發生位翻轉

利用checksum機制,分讀和寫兩種情況來討論:
對於讀,要檢查所讀的所有block的checksum值;
對於寫,分爲append和write。對於append,不檢查checksum,延遲到讀的時候檢查,因爲append的時候,對於最後一個不完整的block計算checksum時候採用的是增量的計算,即使前面存在錯誤,也能在後來的讀發現。對於overwrite,因爲不能採用增量計算,要覆蓋checksum,所以,必須要先檢查只寫入部分數據的checksum是否不一致,否則,數據錯誤會被隱藏。

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