一、引言

首先GFS的設計目標包括易擴展,幾乎線性擴展成大規模的集羣運;行在通用和廉價的硬體上提供可靠的容錯機制並且滿足高吞吐的性能要求。

在論文的開頭就對GFS系統的運行作了假設: 1. 硬體失效是經常發生的,因為伺服器都是通用的相對於廉價的硬體(不過現在Google都已經全部用定製化的企業級硬體了); 2. 數GB到數TB的大文件,Google的大數據場景,數據都非常的大,單個數據文件都非常的大,另外有一點是大文件可以減少文件的數量,降低元數據的數量; 3. 寫一次讀多次,而且幾乎是追加寫,很少覆蓋寫,讀也大塊順序讀較多; 4. 不兼容通用的POSIX文件介面,採用應用與系統一體的設計,這樣他的IO路徑不需要經過內核態的inode等,縮短了IO的路徑。5. 高帶寬比低時延更重要,這是由Google本身的業務場景所決定的;

二、基本架構

GFS主要是包括3種基本角色:client、master、chunk server,client實現各種API以庫的形式提供給業務應用使用;master是全局元數據的管理者,包含name space管理,chunk數據分佈管理等等,它是GFS集羣的核心; chunk server即是實現數據存儲,把文件切分成chunk分成多副本存儲到多個chunk server上實現數據的可靠性;

1. client

client提供的介面不是POSIX介面,主要提供以下create/delete/open/close/read/write/snapshot/record append 等功能介面。client作為api庫鏈接到具體的應用 它不需要經過內核態的inode層。直接通過網路訪問chunk server 或者master更加的高效;它從master獲取metadata並緩存在本地內存中,然後直接與chunk server交互,讀寫IO不必每次都經過master,使master不會成功瓶頸;

client側不提供數據cache機制,主要出於以下原因考慮: 1. 應用場景為大文件的順序讀,需要大內存,而且收益很低; 2. cilent的設計更簡單,client有cache的話需要複雜的機制才能保證多client之間的數據一致性,比如分散式鎖等; 3. chunkserver用的是本地文件系統具有buffer cache,它會緩存經常訪問的數據,這樣chunk server側在一定程度上緩存了熱點數據;

讀流程

查詢是批量查的,比如一次io可能跨多個chunk,那麼多個chunk server的信息包括副本的一次返回,這要可以減少網路交互的次數。

  1. file name + chunk index(offset/chunksize64M) 發給master;
  2. master返回chunk handle id + chunk的所有副本的位置信息(chunk server的ip信息等)給client,client並緩存在本地內存;
  3. client 查找最近的chunk server來發起讀io,根據ip規則知道哪個是最近。
  4. 用chunk handle id 和讀取的範圍[offset + len]。

?

寫流程

為了充分利用chunk server的帶寬,各個副本的chunk server採用串列寫的方式。每一次轉發都是轉發給自己最近的chunk副本,採用流水線操作降低時延,因為網路都是全雙工的,所以數據在接收的同時是可以發送的;

  1. 採用lease機制保證修改的順序,master只會給一個副本chunk分配租約,這個chunk被稱為primary.
  2. 數據全部轉發到那就的chunk server之後,client發消息給primary來決定對該chunk修改的串列順序。
  3. primary把決定的順序發送給chunk server,所有的副本chunk都按照primary定好的順序來修改。
  4. client寫客戶端時不用按順序來寫,數據是先push到各個副本的內存中,然後最後apply的。
  5. 所有的副本chunk成功之後返回到primary,並且primary也成功之後就返回到客戶端成功。
  6. 如果中間有出錯,即做對應的重試,只有重試失敗才最終返回失敗。

原子追加寫

傳統寫是帶有數據與offset的,但是追加寫是僅帶有數據,多個client並發寫的時候,系統至少一次原子將數據追加到文件的後面(類似文件以O_APPEND的方式打開),寫成功之後把file的offset返回給client,這樣上層可以增加一層邏輯構建不同方式的存儲系統,追加寫是現代分散式系統的更願意選擇的方式,因為它的順序寫特性無論對硬碟還是對SSD都是比較友好;另外不是追加寫的話,要支持多client共享寫需要複雜的額外方式來保證,比如分散式鎖等,追加寫用很少的代價就可以保證數據是defined的。

  1. 追加寫與上面的描述的寫流程類似,先把數據push到對應的chunk server上,然後primary chunk先檢查追加寫是否會超出chunk size(64M),如果超過,它先用一部分數據填充對應的chunk上,並告訴副本chunk也做對應的填充,填充之後給client返回對應的offset及通知它剩下的數據需要寫在另外一個chunk上;如果寫的數據不超過chunk size,跟普通寫一樣,寫成功之後結果offset返回給client;
  2. chunk文件以追加寫的方式打開,client A 寫一部分後,client B 去繼續寫,可能會超過chunk size,這時候會給client返回,讓client B寫另外一個chunk.
  3. 寫失敗會重試,這樣可能會導致同一組chunk的副本可能部分或全部包含不同的數據,有重複的記錄等。GFS不保證所有副本的位元組是相同的,它僅保證數據至少原子追加了一次,所以成功寫入的數據一定是報告在所有副本相同的offset寫入了數據,並且最後長度也是一樣的,這樣情況寫入的數據是defined的。

2. master

單一master使設計更加的簡單,單一master並不表示數據只有一份,而是表示單個master擁有全部的元數據,它擁有全局的chunk信息可以更精準的控制數據的分佈,另一方面,讀寫io除首io外幾乎不需要master參與,所以master也不會成為性能瓶頸。

元數據

每個chunk所需的元數據大小小於64位元組,name space 的路徑名前綴用採用壓縮的方式存儲進一步減小元數據的大小,使得所有元數據全部保全在master的內存中,filename到元數據是一個lookup table mapping每個目錄個文件的元數據都分別有讀寫鎖進行保護。主要有4種元數據信息:a. 許可權控制信息;b. file namespace信息;c. file到chunk之間的映射;d. chunk的位置信息。namespace和file到chunk之間的映射以操作日誌的方式記錄在硬碟上;當前的chunk位置信息不持久化在master的上master啟動或者chunk server啟動的時候通報chunk 位置信息。後續master與chunk server之間通過心跳報告chunk狀態,主要原因是伺服器損壞在大規模集羣中是經常發生的事,所以週期去查詢的辦法比固化信息更加簡單來保證集羣的一致性。

chunk的租約

租約用於對chunk的修改,租約有超時時間,在到期前可以續約,可以減輕master的負載。通過chunk server與master之間的週期heartbeat來續約。

垃圾回收

文件刪除之後並不是馬上刪除chunk的,而是有後臺垃慢慢刪除的,這樣的設計更加簡單和可靠,誤刪也有機會恢復。

  1. 用戶刪除的文件記錄operation log並把文件名改成一個hidded 的名字,和記錄刪除的時間,當master掃描metadata發現刪除超過3天(可配置)後,就通知chunk server刪除chunk,刪除file之後在三天之內,還可以通過隱藏的文件名進行訪問,名字改回去則可以恢復。
  2. 孤兒chunk chunk server通過心跳週期上報目前存在的chunk,master檢查chunk handle id是否在metadata中還被使用,如果不被使用即可以告訴chunk server 讓其刪除。
  3. 老舊數據的垃圾回收,通過version num來保證,每次寫分配lease的時候就會更新version num,並通知到所有副本進行持久化更新。如果在上面操作中有副本down掉臨時不可用了,那麼它的version就沒有更新,然後再次起來的時候,master就可以識別出來。

operation log

元數是非常重要的,它是GFS文件系統的核心,需要記錄多份,根據時間線記錄元數據操作的所有記錄,只有操做log持久化成功(所有副本flush成功)以後才修改內存的內容讓client可見,避免持久化異常而導致的不一致。用checkpoint機制使恢復回放的時間盡量短。它是一個簡潔的B tree,可以直接映射到內存,不需要額外的解析。建立checkpoint是需要時間的,它的操作會影響新的修改,解決辦法是另起一個線程建立checkpoint,新的修改另起一個log文件。 建立好checkpoint之後並不是立刻刪掉log文件,它會保存checkpoint附近的log文件,保證操作log的可靠性,比如checkpoint建立不完整。

副本控制

1. 數據分佈的目標:

  1. 可靠和可用性,同一份數據的不同副本分佈在不同的rack上。
  2. 充分利用網路帶寬,跨rack的話,可以充分利用了多交換機的帶寬,讀可以並發到不同的chunk上去讀,不好的是,寫需要跨rack複製,會增加時延,但是GFS是一次寫多讀的應用,所以一個也是相當划算的。

2. 數據分佈只要考慮以下三點:

  1. 根據硬碟的容量。
  2. 限制最近創建的chunk在同一個chunk server上,因為多個臨近的chunk在同一個chunk server上會加大讀的負擔。
  3. 滿足副本在不同的rack上,數據更安全。

3. 副本複製

  1. 對於單副本的複製比多副本的複製有更高的優先順序。
  2. 對於正在使用的副本比將刪除的副本(待垃圾回收的chunk)有很好的優先順序。
  3. 最高優先順序是當前阻塞client操作的chunk。
  4. master限制活動複製的chunk個數,限制複製讀chunk的帶寬,這樣是為了不影響前臺業務IO。

4. 負載均衡

  1. 週期性檢查chunk server的容量和負載,根據容量和負載開遷移chunk數據,使各個chunk server的數據容量和負載更均勻。
  2. 對於新加入的chunk server,遷移數據也是逐步的,而不是一下全部遷移過去的。

3. ChrunkServer

chunk server 上保存具體的數據,每個chunk的大小為64M,每個chunk具有全局的64位的handle id; 每個chunk file又多個備份分佈在不同的chunk server上,一般為3備份,備份數可以根據命名空間區域分別指定。

大塊chunk的優缺點

  • 優點
  1. 讀寫可以減少與master之間的交互,對於大塊的順序讀寫,這個優化是有很明顯好處的。
  2. 大的chunk size 使好幾個操作都在同一個chunksever的chunk可以減少tcp鏈接的額外消耗。
  3. chunk size 大使metadata更小,可以緩存在client 側。
  • 缺點
    1. 小文件可能只有一個chunk,然後一個chunk只存在一個chunkserver上,所以多client訪問的時候為熱點,從而多client同時訪問時帶寬成為瓶頸。短期的解決辦法是提高chunk的副本數,長期的方案是支持client讀取另一個client的數據。

    chunk server執行垃圾回收的優缺點

    • 優點
    1. 由於之前創建chunk 時出現過部分成功部分失敗,然後master並不知道,只有chunk自己清楚,另外delete msg 可能失敗,然後master必須要記住然後重試,如果這樣使設計更為複雜。
    2. chunk server自身掃描減輕了master的壓力,同時通過心跳上報給master進行批量操作,這個消耗可以是錯開分批次的,可以在master比較空閑時處理,能及時響應client的消息。
  • 缺點:
    1. 頻繁的刪除創建file,這樣會導致存儲空間爆滿,而沒有空間。解決辦法:識別同一個文件創建又刪除,即立刻刪除或者針對不同的目錄可以設置不同的策略,某些目錄可以設置成不延遲刪除策略。

    三、數據一致性模型

    1. 文件創建修改原子性

    因為只在master上創建file,所以用namespace lock來保證即可,每個目錄和文件都有讀寫鎖保護,精細化的鎖使master不會成為瓶頸。另外,還有操作日誌來確定它的操作順序。

    2. 文件修改

    文件的修改狀態取決於修改的方式,比如單client還是多client並發寫,是追加寫還是覆蓋寫,是寫成功還是寫失敗。

    1. 單client寫的情況,寫入同一個chunk的數據順序由primary的chunk確定順序,所以寫入成功就可以做到defined的。
    2. 多個client並發寫是consistent的,但不能保證defined的,即寫後上來的內容可能並不是剛寫下去的,可能是別的client寫下去的,比如兩client並發寫同一個chunk的同一個偏移,client寫ab和clientB寫cd,那麼最終的數據可能是ad,cb,ab,cd,前兩者都不是client想要的,所以是undefined的,但,因為各個副本的順序都是由primary的chunk來指定的,無論從哪個副本讀都是一致的。
    3. 同一區域的多個成功的順序修改,有以下兩點保證保證最後一個修改是defined的:
      1. 所有副本的修改順序都是由primary chunk保證一樣的。
      2. 利用chunk version,分配lease的時候更新version num並持久化。master和client都記錄有version num,那麼讀寫和複製的時候chunk側會做校驗,所以能保證得到最新的數據;另外,append only也能檢查chunk是否過期,因為寫的時候會返回offset,如果發現offset比別人的小就肯定是過期了的chunk,需要讓垃圾回收掉。

    3. 客戶端程序保證

    1. 用append write的方式代替overwrite,前面描述了追加寫,在保證寫成功的時候可以保證是defined的,所以GFS的應用全都是用追加寫的方式。
    2. 週期checksum數據,reader只會校驗checkpoint之後的數據,因為之前的數據已經是defined了的。

    附:

    1. consistent是指多個replica的數據是相同的,但可能內容來自多個並發寫入的交叉的,不是真正正確的內容。
    2. defined是指consistent並且不包含交叉。
    3. undefined是表示不同的客戶端在不同的時間點看到不同的數據。

    四、其它

    1. snapshot

    快照採用COW(copy on write)的方式,這種方式具有更快的速度,只有再寫file的時候再copy,不寫的話就是一個引用計數,這也更省空間。

    打快照流程

    1. snapshot的操作請求發master.
    2. master回收該文件所有chunk的lease,確保後續所有的寫操作要問master要租約,這樣master就有機會創建新的chunk。
    3. lease撤銷之後,把打snapshot的操作log記錄到日誌中,並複製內存中的metadata。
    4. 新創建的snapshot 中的元數據中的chunk還是指向源chunk files的, 對這些chunk寫時,client需要向master要lease,這時候master發現要寫的這個chunk有多個引用,這時master會推遲給client迴響應,它要求原chunk C分別在本地copy一份chunk C『出來,本地複製不需要經過網路很快。
    5. 複製完成之後,給master迴響應,master給原來chunk C 引用減一,並原文件指向新的chunk,並且給其中一副本分配lease,最後返回給client,這就跟原來的正常讀寫沒區別了,client也不會感知。

    2. 高可用性

    1. 快速恢復

    1. 進程都有監控,異常以後再秒級之內重啟。
    2. 無論chunk server 還是master的元數據量都比較少,50M到100M的元數據量,從硬碟上恢復只需要幾秒鐘。
    3. master恢復所有的chunk server location信息需要30秒到1分鐘時間。

    2. 數據多副本

    1. chunk 是有多副本的,副本的數量是可以指定的,並且副本的分佈策略是不同的副本分佈在不同的機架上,這樣可以避免,伺服器、電源、TOR交換機故障而導致多副本不可用。
    2. master多機器備份,如果是硬體故障,外部檢測到就在別master副本升級為primary,只需要改DNS就行。剛切換過時,它的狀態和之前的primary有短時間的數據差異,所以這時候集羣只提供讀服務,寫服務需要operation log回放結束後,並同步完chunk server 的chunk 信息之後。

    3. 數據完整性

    1. checksum

    1. 每個chunk按64k切分,然後每64k就有32bit checksum數據。
    2. checksum保存在內存,並持久化在log中,它與用戶的數據是分開存儲的。
    • 讀:
    1. 讀的時候,chunk server是需要校驗checksum的,當發現checksum校驗不過時,不會讓數據擴散,給io請求返回錯誤,並且給master報告。
    2. client收到錯誤會到別的副本讀數據,master會觸發copy正確的副本數據,並把錯數據刪除。
    3. checksum對讀io是有一定的性能影響的,但是GFS的使用場景是大塊讀,這個額外的開銷是可以接受的。
    • 寫:
    1. 追加寫只需要更新後面新寫的block,只需要計算新數據的checksum.
    2. 對於覆蓋寫,前面部分和後面部分都要先verify之後才能寫,不然,如果是覆蓋部分block的,可能之前部分數據是壞了的,而識別不出來。

    2. 後臺掃描

    作用是chunk server對於比較少讀的數據可以提前發現錯誤,避免靜默錯誤造成數據丟失。

    4. 診斷工具

    主要用於問題定位,調試和性能分析;它主要是通過記錄伺服器各種事件。所有的RPC請求和回復消息,除了正在讀寫的IO。RPC日誌是非同步和順序記錄的,消耗很少的資源。


    推薦閱讀:
    相關文章