3 系統(tǒng)交互
我們在設計這個系統(tǒng)時,一個重要的原則是最小化所有操作和 Master 節(jié)點的交互。
帶著這樣的設計理念,我們現(xiàn)在描述一下客戶機、Master 服務器和 Chunk 服務器如何進行交互,以實現(xiàn)
數(shù)據(jù)修改操作、原子的記錄追加操作以及快照功能。
3.1 租約(lease)和變更順序
變更是一個會改變 Chunk 內(nèi)容或者元數(shù)據(jù)的操作,比如寫入操作或者記錄追加操作。變更操作會在 Chunk的所有副本上執(zhí)行。我們使用租約(lease)機制來保持多個副本間變更順序的一致性。Master 節(jié)點為 Chunk的一個副本建立一個租約,我們把這個副本叫做主 Chunk。主 Chunk 對 Chunk 的所有更改操作進行序列化。
所有的副本都遵從這個序列進行修改操作。因此,修改操作全局的順序首先由 Master 節(jié)點選擇的租約的順序
決定,然后由租約中主 Chunk 分配的序列號決定。
設計租約機制的目的是為了最小化 Master 節(jié)點的管理負擔。租約的初始超時設置為 60 秒。不過,只要Chunk 被修改了,主 Chunk 就可以申請更長的租期,通常會得到 Master 節(jié)點的確認并收到租約延長的時間。
這些租約延長請求和批準的信息通常都是附加在 Master 節(jié)點和 Chunk 服務器之間的心跳消息中來傳遞。有時Master 節(jié)點會試圖提前取消租約(例如,Master 節(jié)點想取消在一個已經(jīng)被改名的文件上的修改操作)。即使Master節(jié)點和主Chunk失去聯(lián)系,它仍然可以安全地在舊的租約到期后和另外一個Chunk副本簽訂新的租約。
文件寫入
客戶端嘗試將數(shù)據(jù)寫入到某個 Chunk 的指定位置的過程大致如下:

- 客戶端向 Master 詢問目前哪個 Chunk Server 持有該 Chunk 的 Lease
- Master 向客戶端返回 Primary 和其他 Replica 的位置
- 客戶端將數(shù)據(jù)推送到所有的 Replica 上。Chunk Server 會把這些數(shù)據(jù)保存在緩沖區(qū)中,等待使用
- 待所有 Replica 都接收到數(shù)據(jù)后,客戶端發(fā)送寫請求給 Primary。Primary 為來自各個客戶端的修改操作安排連續(xù)的執(zhí)行序列號,并按順序地應用于其本地存儲的數(shù)據(jù)
- Primary 將寫請求轉發(fā)給其他 Secondary Replica,Replica 們按照相同的順序應用這些修改
- Secondary Replica 響應 Primary,示意自己已經(jīng)完成操作
- Primary 響應客戶端,并返回該過程中發(fā)生的錯誤(若有)
如果該過程有發(fā)生錯誤,可以認為修改已在 Primary 和部分 Secondary 上成功執(zhí)行(如果在 Primary 上就出錯了,那么寫請求不會被轉發(fā)出去)。此時可以認為此次修改操作沒有成功,因為數(shù)據(jù)會處于不一致的狀態(tài)。實際上,GFS 所使用的客戶端 lib 在此時會重新嘗試執(zhí)行此次操作。
值得注意的是,這個流程特意將數(shù)據(jù)流與控制流分開:客戶端先向 Chunk Server 提交數(shù)據(jù),再將寫請求發(fā)往 Primary。這么做的好處在于 GFS 能夠更好地利用網(wǎng)絡帶寬資源。
從上述步驟可見,控制流借由寫請求從客戶端流向 Primary,再流向其他 Secondary Replica。實際上,數(shù)據(jù)流以一條線性數(shù)據(jù)管道進行傳遞的:客戶端會把數(shù)據(jù)上傳到離自己最近的 Replica,該 Replica 在接收到數(shù)據(jù)后再轉發(fā)給離自己最近的另一個 Replica,如此遞歸直到所有 Replica 都能接收到數(shù)據(jù),如此一來便能夠利用上每臺機器的所有出口帶寬。
如果應用程序一次寫入的數(shù)據(jù)量很大,或者數(shù)據(jù)跨越了多個 Chunk,GFS 客戶機代碼會把它們分成多個寫操作。這些操作都遵循前面描述的控制流程,但是可能會被其它客戶機上同時進行的操作打斷或者覆蓋。
因此,共享的文件 region 的尾部可能包含來自不同客戶機的數(shù)據(jù)片段,盡管如此,由于這些分解后的寫入操作在所有的副本上都以相同的順序執(zhí)行完成,Chunk 的所有副本都是一致的。這使文件 region 處于 2.7 節(jié)描述的一致的、但是未定義的狀態(tài)。
3.3 原子的記錄追加
文件追加操作的過程和寫入的過程有幾分相似:
- 客戶端將數(shù)據(jù)推送到每個 Replica,然后將請求發(fā)往 Primary
- Primary 首先判斷將數(shù)據(jù)追加到該塊后是否會令塊的大小超過上限:如果是,那么 Primary 會為該塊寫入填充至其大小達到上限,并通知其他 Replica 執(zhí)行相同的操作,再響應客戶端,通知其應在下一個塊上重試該操作
- 如果數(shù)據(jù)能夠被放入到當前塊中,那么 Primary 會把數(shù)據(jù)追加到自己的 Replica 中,拿到追加成功返回的偏移值,然后通知其他 Replica 將數(shù)據(jù)寫入到該偏移位置中
- 最后 Primary 再響應客戶端
當追加操作在部分 Replica 上執(zhí)行失敗時,Primary 會響應客戶端,通知它此次操作已失敗,客戶端便會重試該操作。和寫入操作的情形相同,此時已有部分 Replica 順利寫入這些數(shù)據(jù),重新進行數(shù)據(jù)追加便會導致這一部分的 Replica 上出現(xiàn)重復數(shù)據(jù),不過 GFS 的一致性模型也確實并未保證每個 Replica 都會是完全一致的。
GFS 只確保數(shù)據(jù)會以一個原子的整體被追加到文件中至少一次。由此我們可以得出,當追加操作成功時,數(shù)據(jù)必然已被寫入到所有 Replica 的相同偏移位置上,且每個 Replica 的長度都至少超出此次追加的記錄的尾部,下一次的追加操作必然會被分配一個比該值更大的偏移值,或是被分配到另一個新的塊上。
3.4 文件快照
GFS 還提供了文件快照操作,可為指定的文件或目錄創(chuàng)建一個副本。
快照操作的實現(xiàn)采用了寫時復制(Copy on Write)的思想:
- 在 Master 接收到快照請求后,它首先會撤回這些 Chunk 的 Lease,以讓接下來其他客戶端對這些 Chunk 進行寫入時都會需要請求 Master 獲知 Primary 的位置,Master 便可利用這個機會創(chuàng)建新的 Chunk
- 當 Chunk Lease 撤回或失效后,Master 會先寫入日志,然后對自己管理的命名空間進行復制操作,復制產(chǎn)生的新記錄指向原本的 Chunk
- 當有客戶端嘗試對這些 Chunk 進行寫入時,Master 會注意到這個 Chunk 的引用計數(shù)大于 1。此時,Master 會為即將產(chǎn)生的新 Chunk 生成一個 Handle,然后通知所有持有這些 Chunk 的 Chunk Server 在本地復制出一個新的 Chunk,應用上新的 Handle,然后再返回給客戶端
4.1 Master Namespace 管理和鎖
在前面我們已經(jīng)了解到,Namespace 作為 GFS 元信息的一部分會被維持在 Master 的內(nèi)存中,由 Master 負責管理。在邏輯上,GFS Master 并不會根據(jù)文件與目錄的關系以分層的結構來管理這部分數(shù)據(jù),而是單純地將其表示為從完整路徑名到對應文件元數(shù)據(jù)的映射表,并在路徑名上應用前綴壓縮以減少內(nèi)存占用。
為了管理來自不同客戶端的并發(fā)請求對 Namespace 的修改,Master 會為 Namespace 中的每個文件和目錄都分配一個讀寫鎖(Read-Write Lock)。由此,對不同 Namespace 區(qū)域的并發(fā)請求便可以同時進行。
所有 Master 操作在執(zhí)行前都會需要先獲取一系列的鎖:通常,當操作涉及某個路徑 /d1/d2/.../dn/leaf 時,Master 會需要先獲取從 /d1、/d1/d2 到 /d1/d2/.../dn 的讀鎖,然后再根據(jù)操作的類型獲取 /d1/d2/.../dn/leaf 的讀鎖或寫鎖 —— 獲取父目錄的讀鎖是為了避免父目錄在此次操作執(zhí)行的過程中被重命名或刪除。
由于大量的讀寫鎖可能會造成較高的內(nèi)存占用,這些鎖會在實際需要時才進行創(chuàng)建,并在不再需要時被銷毀。除外,所有的鎖獲取操作也會按照一個相同的順序進行,以避免發(fā)生死鎖:鎖首先按 Namespace 樹的層級排列,同一層級內(nèi)則以路徑名字典序排列。
4.2 副本的位置
為了進一步優(yōu)化 GFS 集群的效率,Master 在 Replica 的位置選取上會采取一定的策略。
Master 的 Replica 編排策略主要為了實現(xiàn)兩個目標:最大化數(shù)據(jù)的可用性,以及最大化網(wǎng)絡帶寬的利用率。為此,Replica 不僅需要被保存在不同的機器上,還會需要被保存在不同的機架上,這樣如果整個機架不可用了,數(shù)據(jù)仍然得以存活。如此一來,不同客戶端對同一個 Chunk 進行讀取時便可以利用上不同機架的出口帶寬,但壞處就是進行寫入時數(shù)據(jù)也會需要在不同機架間流轉,不過在 GFS 的設計者看來這是個合理的 trade-off。
4.3 創(chuàng)建,重新復制,重新負載均衡
Replica 的生命周期轉換操作實際只有兩個:創(chuàng)建和刪除。首先,Replica 的創(chuàng)建可能源于以下三種事件:創(chuàng)建 Chunk、為 Chunk 重備份、以及 Replica 均衡。
在 Master 創(chuàng)建一個新的 Chunk 時,首先它會需要考慮在哪放置新的 Replica。Master 會考慮如下幾個因素:
Master 會傾向于把新的 Replica 放在磁盤使用率較低的 Chunk Server 上
Master 會傾向于確保每個 Chunk Server 上“較新”的 Replica 不會太多,因為新 Chunk 的創(chuàng)建意味著接下來會有大量的寫入,如果某些 Chunk Server 上有太多的新 Chunk Replica,那么寫操作壓力就會集中在這些 Chunk Server 上
如上文所述,Master 會傾向于把 Replica 放在不同的機架上
當某個 Chunk 的 Replica 數(shù)量低于用戶指定的閾值時,Master 就會對該 Chunk 進行重備份。這可能是由 Chunk Server 失效、Chunk Server 回報 Replica 數(shù)據(jù)損壞或是用戶提高了 Replica 數(shù)量閾值所觸發(fā)。
首先,Master 會按照以下因素為每個需要重備份的 Chunk 安排優(yōu)先級:
該 Chunk 的 Replica 數(shù)距離用戶指定的 Replica 數(shù)量閾值的差距有多大
優(yōu)先為未刪除的文件(見下文)的 Chunk 進行重備份
除外,Master 還會提高任何正在阻塞用戶操作的 Chunk 的優(yōu)先級
有了 Chunk 的優(yōu)先級后,Master 會選取當前擁有最高優(yōu)先級的 Chunk,并指定若干 Chunk Server 直接從現(xiàn)在已有的 Replica 上復制數(shù)據(jù)。Master 具體會指定哪些 Chunk Server 進行復制操作同樣會考慮上面提到的幾個因素。除外,為了減少重備份對用戶使用的影響,Master 會限制當前整個集群正在進行的復制操作的數(shù)量,同時 Chunk Server 也會限制復制操作所使用的帶寬。
最后,Master 會周期地檢查每個 Chunk 當前在集群內(nèi)的分布情況,并在必要時遷移部分 Replica 以更好地均衡各節(jié)點的磁盤利用率和負載。新 Replica 的位置選取策略和上面提到的大體相同,除此以外 Master 還會需要選擇要移除哪個已有的 Replica:簡單概括的話,Master 會傾向于移除磁盤占用較高的 Chunk Server 上的 Replica,以均衡磁盤使用率。
4.4 垃圾回收
GFS 在文件刪除后不會立刻回收可用的物理空間。GFS 空間回收采用惰性的策略,只在文件和 Chunk 級的常規(guī)垃圾收集時進行。我們發(fā)現(xiàn)這個方法使系統(tǒng)更簡單、更可靠。
4.4.1 機制
當一個文件被應用程序刪除時,Master 節(jié)點象對待其它修改操作一樣,立刻把刪除操作以日志的方式記錄下來。但是,Master 節(jié)點并不馬上回收資源,而是把文件名改為一個包含刪除時間戳的、隱藏的名字。當Master 節(jié)點對文件系統(tǒng)命名空間做常規(guī)掃描的時候,它會刪除所有三天前的隱藏文件(這個時間間隔是可以
設置的)。直到文件被真正刪除,它們?nèi)耘f可以用新的特殊的名字讀取,也可以通過把隱藏文件改名為正常顯示的文件名的方式“反刪除”。當隱藏文件被從名稱空間中刪除,Master 服務器內(nèi)存中保存的這個文件的相關元數(shù)據(jù)才會被刪除。這也有效的切斷了文件和它包含的所有 Chunk 的連接。
在對 Chunk 名字空間做類似的常規(guī)掃描時,Master 節(jié)點找到孤兒 Chunk(不被任何文件包含的 Chunk)并刪除它們的元數(shù)據(jù)。Chunk 服務器在和 Master 節(jié)點交互的心跳信息中,報告它擁有的 Chunk 子集的信息,Master 節(jié)點回復 Chunk 服務器哪些 Chunk 在 Master 節(jié)點保存的元數(shù)據(jù)中已經(jīng)不存在了。Chunk 服務器可以任意刪除這些 Chunk 的副本。
采用這種刪除機制主要有如下三點好處:
對于大規(guī)模的分布式系統(tǒng)來說,這樣的機制更為可靠:在 Chunk 創(chuàng)建時,創(chuàng)建操作可能在某些 Chunk Server 上成功了,在其他 Chunk Server 上失敗了,這導致某些 Chunk Server 上可能存在 Master 不知道的 Replica。除此以外,刪除 Replica 的請求可能會發(fā)送失敗,Master 會需要記得嘗試重發(fā)。相比之下,由 Chunk Server 主動地刪除 Replica 能夠以一種更為統(tǒng)一的方式解決以上的問題
這樣的刪除機制將存儲回收過程與 Master 日常的周期掃描過程合并在了一起,這就使得這些操作可以以批的形式進行處理,以減少資源損耗;除外,這樣也得以讓 Master 選擇在相對空閑的時候進行這些操作
用戶發(fā)送刪除請求和數(shù)據(jù)被實際刪除之間的延遲也有效避免了用戶誤操作的問題
不過,如果在存儲資源較為稀缺的情況下,用戶對存儲空間使用的調優(yōu)可能就會受到該機制的阻礙。為此,GFS 允許客戶端再次指定刪除該文件,以確實地從 Namespace 層移除該文件。除外,GFS 還可以讓用戶為 Namespace 中不同的區(qū)域指定不同的備份和刪除策略,如限制 GFS 不對某個目錄下的文件進行 Chunk 備份等。
4.5 過期失效的副本檢測
當 Chunk 服務器失效時,Chunk 的副本有可能因錯失了一些修改操作而過期失效。Master 節(jié)點保存了每個 Chunk 的版本號,用來區(qū)分當前的副本和過期副本。
無論何時,只要 Master 節(jié)點和 Chunk 簽訂一個新的租約,它就增加 Chunk 的版本號,然后通知最新的副本。Master 節(jié)點和這些副本都把新的版本號記錄在它們持久化存儲的狀態(tài)信息中。這個動作發(fā)生在任何客戶機得到通知以前,因此也是對這個 Chunk 開始寫之前。如果某個副本所在的 Chunk 服務器正好處于失效狀態(tài),那么副本的版本號就不會被增加。Master 節(jié)點在這個 Chunk 服務器重新啟動,并且向 Master 節(jié)點報告它擁有的 Chunk 的集合以及相應的版本號的時候,就會檢測出它包含過期的 Chunk。如果 Master 節(jié)點看到一個比它記錄的版本號更高的版本號,Master 節(jié)點會認為它和 Chunk 服務器簽訂租約的操作失敗了,因此會選擇更高的版本號作為當前的版本號。
Master 節(jié)點在例行的垃圾回收過程中移除所有的過期失效副本。在此之前Master 節(jié)點在回復客戶機的Chunk 信息請求的時候,簡單的認為那些過期的塊根本就不存在。另外一重保障措施是,Master 節(jié)點在通知客戶機哪個 Chunk 服務器持有租約、或者指示 Chunk 服務器從哪個 Chunk 服務器進行克隆時,消息中都附帶
了 Chunk 的版本號??蛻魴C或者 Chunk 服務器在執(zhí)行操作時都會驗證版本號以確??偸窃L問當前版本的數(shù)據(jù)
5 容錯和診斷
CHUNK 復制
每個 Chunk 都被復制到不同機架上的不同的 Chunk 服務器上。用戶可以為文件命名空間的不同部分設定不同的復制級別。缺省是 3。當有 Chunk 服務器離線了,或者通過 Chksum 校驗(參考 5.2節(jié))發(fā)現(xiàn)了已經(jīng)損壞的數(shù)據(jù),Master 節(jié)點通過克隆已有的副本保證每個 Chunk 都被完整復制。雖然 Chunk復制策略對我們非常有效,但是我們也在尋找其它形式的跨服務器的冗余解決方案,比如使用奇偶校驗、或者 Erasure codes來解決我們?nèi)找嬖鲩L的只讀存儲需求。我們的系統(tǒng)主要的工作負載是追加方式的寫入和讀取操作,很少有隨機的寫入操作,因此,我們認為在我們這個高度解耦合的系統(tǒng)架構下實現(xiàn)這些復雜的冗余方案很有挑戰(zhàn)性,但并非不可實現(xiàn)。
MASTER 復制
前面我們提到,Master 會以先寫日志(Operation Log)的形式對集群元數(shù)據(jù)進行持久化:日志在被確實寫出前,Master 不會對客戶端的請求進行響應,后續(xù)的變更便不會繼續(xù)執(zhí)行;除外,日志還會被備份到其他的多個機器上,日志只有在寫入到本地以及遠端備份的持久化存儲中才被視為完成寫出。
在重新啟動時,Master 會通過重放已保存的操作記錄來恢復自身的狀態(tài)。為了保證 Master 能夠快速地完成恢復,Master 會在日志達到一定大小后為自身的當前狀態(tài)創(chuàng)建 Checkpoint(檢查點),并刪除 Checkpoing 創(chuàng)建以前的日志,重啟時便從最近一次創(chuàng)建的 Checkpoint 開始恢復。Checkpoint 文件的內(nèi)容會以 B 樹的形式進行組織,且在被映射到內(nèi)存后便能夠在不做其他額外的解析操作的情況下檢索其所存儲的 Namespace,這便進一步減少了 Master 恢復所需的時間。
為了簡化設計,同一時間只會有一個 Master 起作用。當 Master 失效時,外部的監(jiān)控系統(tǒng)會偵測到這一事件,并在其他地方重新啟動新的 Master 進程。
除外,集群中還會有其他提供只讀功能的 Shadow Master:它們會同步 Master 的狀態(tài)變更,但有可能延遲若干秒,其主要用于為 Master 分擔讀操作的壓力。Shadow Master 會通過讀取 Master 操作日志的某個備份來讓自己的狀態(tài)與 Master 同步;它也會像 Master 那樣,在啟動時輪詢各個 Chunk Server,獲知它們所持有的 Chunk Replica 信息,并持續(xù)監(jiān)控它們的狀態(tài)。實際上,在 Master 失效后,Shadow Master 仍能為整個 GFS 集群提供只讀功能,而 Shadow Master 對 Master 的依賴只限于 Replica 位置的更新事件。
5.2 數(shù)據(jù)完整性
如前面所述,每個 Chunk 都會以 Replica 的形式被備份在不同的 Chunk Server 中,而且用戶可以為 Namespace 的不同部分賦予不同的備份策略。
為了保證數(shù)據(jù)完整,每個 Chunk Server 都會以校驗和的形式來檢測自己保存的數(shù)據(jù)是否有損壞;在偵測到損壞數(shù)據(jù)后,Chunk Server 也可以利用其它 Replica 來恢復數(shù)據(jù)。
首先,Chunk Server 會把每個 Chunk Replica 切分為若干個 64KB 大小的塊,并為每個塊計算 32 位校驗和。和 Master 的元數(shù)據(jù)一樣,這些校驗和會被保存在 Chunk Server 的內(nèi)存中,每次修改前都會用先寫日志的形式來保證可用。當 Chunk Server 接收到讀請求時,Chunk Server 首先會利用校驗和檢查所需讀取的數(shù)據(jù)是否有發(fā)生損壞,如此一來 Chunk Server 便不會把損壞的數(shù)據(jù)傳遞給其他請求發(fā)送者,無論它是客戶端還是另一個 Chunk Server。發(fā)現(xiàn)損壞后,Chunk Server 會為請求發(fā)送者發(fā)送一個錯誤,并向 Master 告知數(shù)據(jù)損壞事件。接收到錯誤后,請求發(fā)送者會選擇另一個 Chunk Server 重新發(fā)起請求,而 Master 則會利用另一個 Replica 為該 Chunk 進行重備份。當新的 Replica 創(chuàng)建完成后,Master 便會通知該 Chunk Server 刪除這個損壞的 Replica。
當進行數(shù)據(jù)追加操作時,Chunk Server 可以為位于 Chunk 尾部的校驗和塊的校驗和進行增量式的更新,或是在產(chǎn)生了新的校驗和塊時為其計算新的校驗和。即使是被追加的校驗和塊在之前已經(jīng)發(fā)生了數(shù)據(jù)損壞,增量更新后的校驗和依然會無法與實際的數(shù)據(jù)相匹配,在下一次讀取時依然能夠檢測到數(shù)據(jù)的損壞。在進行數(shù)據(jù)寫入操作時,Chunk Server 必須讀取并校驗包含寫入范圍起始點和結束點的校驗和塊,然后進行寫入,最后再重新計算校驗和。
除外,在空閑的時候,Chunk Server 也會周期地掃描并校驗不活躍的 Chunk Replica 的數(shù)據(jù),以確保某些 Chunk Replica 即使在不怎么被讀取的情況下,其數(shù)據(jù)的損壞依然能被檢測到,同時也確保了這些已損壞的 Chunk Replica 不至于讓 Master 認為該 Chunk 已有足夠數(shù)量的 Replica。
FAQ
Q:為什么原子記錄追加操作是至少一次(At Least Once),而不是確定一次(Exactly Once)?
要讓追加操作做到確定一次是不容易的,因為如此一來 Primary 會需要保存一些狀態(tài)信息以檢測重復的數(shù)據(jù),而這些信息也需要復制到其他服務器上,以確保 Primary 失效時這些信息不會丟失。在 Lab 3 中你會實現(xiàn)確定一次的行為,但用的是比 GFS 更復雜的協(xié)議(Raft)。
Q:應用怎么知道 Chunk 中哪些是填充數(shù)據(jù)或者重復數(shù)據(jù)?
要想檢測填充數(shù)據(jù),應用可以在每個有效記錄之前加上一個魔數(shù)(Magic Number)進行標記,或者用校驗和保證數(shù)據(jù)的有效性。應用可通過在記錄中添加唯一 ID 來檢測重復數(shù)據(jù),這樣應用在讀入數(shù)據(jù)時就可以利用已經(jīng)讀入的 ID 來排除重復的數(shù)據(jù)了。GFS 本身提供了 library 來支撐這些典型的用例。
Q:考慮到原子記錄追加操作會把數(shù)據(jù)寫入到文件的一個不可預知的偏移值中,客戶端該怎么找到它們的數(shù)據(jù)?
追加操作(以及 GFS 本身)主要是面向那些會完整讀取文件的應用的。這些應用會讀取所有的記錄,所以它們并不需要提前知道記錄的位置。例如,一個文件中可能包含若干個并行的網(wǎng)絡爬蟲獲取的所有鏈接 URL。這些 URL 在文件中的偏移值是不重要的,應用只會想要完整讀取所有 URL。
Q:如果一個應用使用了標準的 POSIX 文件 API,為了使用 GFS 它會需要做出修改嗎?
答案是需要的,不過 GFS 并不是設計給已有的應用的,它主要面向的是新開發(fā)的應用,如 MapReduce 程序。
Q:GFS 是怎么確定最近的 Replica 的位置的?
論文中有提到 GFS 是基于保存 Replica 的服務器的 IP 地址來判斷距離的。在 2003 年的時候,Google 分配 IP 地址的方式應該確保了如果兩個服務器的 IP 地址在 IP 地址空間中較為接近,那么它們在機房中的位置也會較為接近。
Q:Google 現(xiàn)在還在使用 GFS 嗎?
Google 仍然在使用 GFS,而且是作為其他如 BigTable 等存儲系統(tǒng)的后端。由于工作負載的擴大以及技術的革新,GFS 的設計在這些年里無疑已經(jīng)經(jīng)過大量調整了,但我并不了解其細節(jié)。HDFS 是公眾可用的對 GFS 的設計的一種效仿,很多公司都在使用它。
Q:Master 不會成為性能瓶頸嗎?
確實有這個可能,GFS 的設計者也花了很多心思來避免這個問題。例如,Master 會把它的狀態(tài)保存在內(nèi)存中以快速地進行響應。從實驗數(shù)據(jù)來看,對于大文件讀取(GFS 主要針對的負載類型),Master 不是瓶頸所在;對于小文件操作以及目錄操作,Master 的性能也還跟得上(見 6.2.4 節(jié))。
Q:GFS 為了性能和簡潔而犧牲了正確性,這樣的選擇有多合理呢?
這是分布式系統(tǒng)領域的老問題了。保證強一致性通常需要更加復雜且需要機器間進行更多通信的協(xié)議(正如我們會在接下來幾門課中看到的那樣)。通過利用某些類型的應用可以容忍較為松懈的一致性的事實,人們就能夠設計出擁有良好性能以及足夠的一致性的系統(tǒng)。例如,GFS 對 MapReduce 應用做出了特殊優(yōu)化,這些應用需要的是對大文件的高讀取效率,還能夠容忍文件中存在數(shù)據(jù)空洞、重復記錄或是不一致的讀取結果;另一方面,GFS 則不適用于存儲銀行賬號的存款信息。
Q:如果 Master 失效了會怎樣?
GFS 集群中會有持有 Master 狀態(tài)完整備份的 Replica Master;通過論文中沒有提到的某個機制,GFS 會在 Master 失效時切換到其中一個 Replica(見 5.1.3 節(jié))。有可能這會需要一個人類管理者的介入來指定一個新的 Master。無論如何,我們都可以確定集群中潛伏著一個故障單點,理論上能夠讓集群無法從 Master 失效中進行自動恢復。我們會在后面的課程中學習如何使用 Raft 協(xié)議實現(xiàn)可容錯的 Master。
回答開頭的那個問題
由失效后重啟的 Chunk Server + 客戶端緩存的 Chunk 位置數(shù)據(jù)導致客戶端讀取到過時的文件內(nèi)容
和由于 Shadow Master 讀取到的過時文件元信息
以上是保證所有寫入操作都成功時客戶端可能讀取到過時數(shù)據(jù)的兩種情況 —— 如果有寫入操作失敗,數(shù)據(jù)會進入不確定的狀態(tài),自然客戶端也有可能讀取到過時或是無效的數(shù)據(jù)。
我的收獲與啟發(fā)
- GFS的論文的信息量真的十分龐大,我精讀了3遍,也未能很好的消化。
- 為了簡化設計,GFS引入了單MASTER的架構。然后就是也防止單點負載過高的問題。GFS做了如下努力,只把關鍵信息放在MASTER內(nèi)存里,其余的讓MASTER在多個REPLICA里選出PRIMARY去和CLIENT交互。
- 容錯依賴于很多,最重要的是復制,也就是冗余。還有CHECKSUM機制。以及MASTER的定期發(fā)送心跳驗活同時在心跳里檢查副本是否被污染,或數(shù)據(jù)不平衡。
- MASTER則自身建立了常規(guī)DB的操作,LOG + CHECK POINT來保證可以最小化MASTER的信息丟失。 引入了SHADOW MASTER 來臨時擔負起讀的工作。
- 這篇文章里用到了租約,其目的是為了只有一個CHUNK可以作為主CHUNK。
- 主CHUNK的作用是為了統(tǒng)一寫入順序還有分擔MASTER的壓力。
- 刪除用的是LAZY 刪除機制,不是立即刪除,而是做個標記,最后交給垃圾收集器去做。這樣做的好處有防止用戶誤操作。 在分布式環(huán)境下這種機制更可靠。 還有可以在MASTER相對空閑的時候,用批處理的形式更高效的刪除。
- 文件的快照操作,利用寫時復制的思想。