- 為了性能 (Performance), 所以將數據分割放到大量的伺服器上,從而實現並行的讀取數據,這就是分片 (Sharding).
- 而成敗上千的機器總會發生錯誤,所以有了容錯 (Fault Tolerance).
- 實現容錯最簡單的方式就是複製 (Replication), 其中一個發生故障了就切換另一個.
- 使用了複製,如果你不夠小心,那麼它們之間就可能會不一致。數據就有可能出現問題,所以就有了不一致的問題 (Inconsistency).
- 如果為了實現一致性 (Consistency), 那麼就需要多進行額外的互動來保證一致性,所以代價就是低性能 (Low Perf)
, 但這與我們開始的希望不符合.
{{< block type="tip">}}
所以,強一致性代表著低性能.
{{< /block >}}
設計目標#
- 由於 GFS 是建立在大量的計算機上的,而這些計算機會不可避免的發生故障。所以必須要進行:檢查,容錯以及快速從故障恢復.
- 主要支持大文件(例如說好幾 G 的文件), 同時也支持小文件但不做針對性的優化.
- 工作負載主要由兩種類型的讀取組成:大的流式讀取和小的隨機讀取
. 對於性能有過特別考慮的應用通常會作批處理並且對他們讀取的內容進行排序,這樣可以使得他們的讀取始終是單向順序讀取,而不需要往回讀取數據.- 在大的流式讀取中,單個操作通常要讀取數百 k, 甚至 1m 或者更大的數據。對於同一個客戶端來說,往往會發起連續的讀取操作順序讀取一個文件.
- 小的隨機讀取通常在某個任意的偏移位置讀取幾 kb 的數據。小規模的隨機讀取通常在文件的不同位置,讀取幾 k 數據.
- GFS 中的文件通常上一旦完成寫入就很少會再次修改,所以主要針對大的流式讀取, 同時也支持任意位置的小規模寫入操作.
- GFS 對多個客戶端並行添加同一個文件必須要有非常有效且明確語義的支持,即原子操作. 通常會有多個客戶端會並行的對同一個文件進行 append.
- 高性能的穩定帶寬的網絡要比低延遲更加重要。我們大多數的目標應用程序都非常重視高速批量處理數據
, 而很少有人對單個讀寫操作有嚴格的響應時間要求.
架構#
- 單個 master, 多個 chunk server (保存具體的文件), 多個 client.
- 每個文件被拆分為一定大小 (64mb) 的塊 (chunk), 且每個 chunk 有一個唯一的 64 位的標誌 (chunk handle).
- 每個 chunk 都會在不同的 chunk server 上保存備份 (默認是 3 個), 用戶可以指定不同的複製級別.
- master 管理元數據 (metadata), 例如文件到 chunk 的映射關係,chunk 的位置信息等.
- master 管理 chunk 的分片,孤點 chunk 的垃圾回收機制,chunk server 之間的鏡像管理等
- 每個 chunk server 與 master 之間有心跳機制,並在檢測的過程中發出指令並收集狀態.
GFS Master 中的 metadata#
- filename -> chunk ids(chunk handles) NV
- chunk handle 與 chunk 數據的對應關係
- chunk 保存在哪個伺服器上 (chunk server list)
- chunk 的 version no NV
- chunk 的 primary chunk server, 因為寫操作在其上進行
- primary chunk server 的 lease expiration
這兩個 data table 都在 master 的內存中存放,為了容錯 (例如說重啟後數據不丟失數據), 它會在磁碟上存儲 log, 讀取的使用從內存裡面讀取,寫的時候會寫入內存以及磁碟.
每當有數據變更時,就會在磁碟上的日誌進行追加,並且定期 (日誌增長超過某一個大小) 創建 checkpoint (類似快照,不用從頭開始讀取)
GFS Read Steps#
- 首先讀請求就表明 client 有 filename 以及想要讀取的位置 (offset), 然後發送給 master.
- master 收到請求後就從 filenames 中獲取對應的 chunk handles. 而每個 chunk 的大小上固定的,所以就得到的具體開始的 chunk handle.
- 然後根據 chunk handle 找到對應存放數據的 chunk server 的列表返回給 client.
- client 可以選擇一個 server 來進行讀取 (論文中說會選擇一個最近的伺服器,因為 google 裡面 ip 是連續的,可以根據 ip 判斷遠近)
, 因為客戶端每次只讀取 1mb 或者 64kb 的數據,所以它會緩存 chunk 與 chunk server 的關係,這樣就不用每次都請求. - chunk server 收到請求後,根據 chunk handle (推測 chunk 是安裝 chunk handle 進行命名的) 找到對應的 chunk 以及 offset 對應的數據給客戶端.
q1: 如果讀取的數據跨越了一個 chunk 怎麼辦?#
例如說 client 想要讀取的數據超過了 64mb, 或者僅僅上是 2 個 byte 卻跨越了 chunk,client 會在發送請求時注意到這次請求跨越了邊界,
所以會將一個請求拆分為 2 個請求發送到 master, 所以這裡可能上向 master 發送兩次讀請求,之後在向不同的 chunk server 讀取數據.
多個副本之間變更順序的一致性#
針對一個 chunk
- master 授權給某個持有這個 chunk 的 server 一個租約期限 (60s), 稱為 primary.
- primary 對所有的更改操作進行排序 (serial order), 然後其他的 secondary 根據這個順序進行變更.
- 只要這個 chunk 正在變更,那麼 primary 就可以向 master 申請延長租約.
GFS Write Steps#
- client 向 master 發送請求獲取 chunk server list (primary,secondaries),
如果沒有 primary,master 就會選擇一個 secondary 成為 primary. - client 獲取到 chunk server list 後會緩存下來,只有當 primary
沒有響應或租約過期後才會再次請求. - client 將數據推送到所有 replicas, 客戶端不保證推送的順序,每個 chunk server 會將數據保存在內部的 lur cache 中,直到數據被使用或過期.
- 當所有 replicas 都收到了數據,client 將會發送一個寫請求到 primary, 它標識了之前推送到每個副本的數據.
primary 將這些寫入組織成一定的順序應用到自己本地. - primary 然後將這個應用順序轉發給各個 secondary.
- secondaries 應用這個順序完成修改並答復 primary.
- primary 答復 client, 如果出現了任意錯誤也會答復給 client. 在出現錯誤的情況下,write request 也可能在 primary 以及 secondary 中成功
(如果 primary 直接就失敗了,那麼它將不會轉發 serial order 給 secondaries),client 將認為這次請求是失敗的,它會通過重試來處理 (
3-7 嘗試幾次重新寫入)
GFS Atomic Record Appends#
{{<block type="tip" title="對同一片區域個並發寫入是不可序列化的">}}
這片區域可能最終包含多個客戶端的數據片段.
{{< /block >}}
一個原子的 append 操作.recored append
至少會在給定的 offset (GFS 自己選擇的,因為這裡可能會失敗,可能有一些 chunk server 上有這個數據)
上追加到文件上一次,並將該 offset 返回給 client. 它類似O_APPEND
保證原子性.
recored append
遵守 GFS Write Steps 流程,但是有一些特別的地方:
- client 推送所有數據後,primary 會檢查 append 到該 chunk 後是否超過了單個 chunk 的大小.
- 如果超過了,則在當前 chunk 填充到最大 offset 時 (secondary 也要保存), 回覆 client, 指出該操作應該在下一個 chunk 上重試 (
record 的大小需要控制在單個 chunk 最大值的四分之一,以保證碎片在可接收的水平). - 如果沒有超過最大大小,則按照正常的情況進行保存.
過期副本檢測#
如果 chunk server 發生故障而宕機或者丟失了某些更新請求,那麼它就有可能過期了。對於每個 chunk,master 都維護了一個 version
no 來標識最新和過期的副本.
當 master 為一個 chunk 的 primary server 授權或續期時就會增加 version no 並通知所有 replicas 進行更新.
在數據一致的情況下,master 和所有 replicas 的 version no 是一致的 (在 client 發送寫請求之前可以保證).
當 chunk server 重啟或上報 version no 時,master 會檢查它是否包含過期的副本,如果發現 master 發現 version
no 大於它的記錄,master 會採用更高的 version no 進行更新.
master 通過周期性的垃圾回收來刪除過期的副本,在刪除前,它會確認在它所有 client 的 chunk 信息請求的應答中沒有包含這個過期的副本.
client 在從 master 獲取 chunk server 列表時會附帶獲取 version no, 所以它可以進行比對,選擇最新的副本進行操作.
總結#
這並不是一個合格的多副本,多活,高可用,故障自修復的分佈式系統.