活動

河北十一选五数学计算题:從 GFS 失敗的架構設計來看一致性的重要性

廣告
廣告

微信掃一掃,分享到朋友圈

從 GFS 失敗的架構設計來看一致性的重要性
0 0

作者簡介 陳東明,餓了么北京技術中心架構組負責人,負責餓了么的產品線架構設計以及餓了么基礎架 構研發工作。曾任百度架構師,負責百度即時通訊產品的架構設計。具有豐富的大規模系統構 建和基礎架構的研發經驗,善于復雜業務需求下的大并發、分布式系統設計和持續優化。作者微信公眾號dongming_cdm,歡迎關注。

GFS(Google File System)是Google公司開發的一款分布式文件系統。

在2003年,Google 發表一篇論文詳細描述了GFS的架構。GFS,MapReduce,Bigt able并稱為Google的三架? ?,推動了Google的高速發展。其他互聯公司和開源領域紛紛模仿,構建自己的系統??梢?這么說,GFS,MapReduce,Bigt able引領了互聯網公司的分布式技術的發展。但GFS架構設 計并不是一個完美的架構設計,它有諸多方面的問題,一致性的欠缺就是其中的一個問題。

本文探討一下GFS架構設計、分析其在一致性方面的設計不足,并且看一下一致性對分布式系統 的重要性。

我們從GFS的接口設計說起。

接口

GFS采用了人們非常熟悉的接口,但是被沒有實現如POSIX的標準接口。通常的一些操作包 括:create, delete, open, close, read, write, record append等。create,delete,open,close 和POSIX的接口類似,這里就不強調說了。這里詳細講述一下write, record append提供的語意。

  • write操作可以將任意?度len的數據寫入到任意指定文件的位置off set
  • record append操作可以原子的將len<=16MB的數據寫入到指定文件的末尾

之所以GFS設計這個接口,是因為record append不是簡單的offset等于文件末尾的write操作。 record append是具有原子特性的操作,本文后面會詳細解釋這個原子特性。

write和record append操作都允許多個客戶端并發操作。

架構

GFS架構如下圖。(摘自GFS的論文)

主要的架構部件有GFS client, GFS master, GFS chunkserver。一個GFS集群包括:一個 master,多個chunkserver,集群可以被多個GFS client訪問。

GFS客戶端(GFS client)是運行在應用(Application)進程里的代碼,通常以SDK形式 存在。

GFS中的文件被分割成固定大小的塊(chunk),每個塊的?度是固定64MB。GFS chunkserver把這些chunk存儲在本地的Linux文件系統中,也就是本地磁盤中。通常每個 chunck會被保存3個副本(replica)。一個chunkserver會保存多個塊,每個塊都會有一個 標識叫做塊柄(chunk handle)

GFS 主(mast er)維護文件系統的元數據(met adat a),包括名字空間(namespace, 也就是常規文件系統的中的文件樹),訪問控制信息,每個文件有哪些chunk構成,chunk 存儲在哪個chunkserver上,也就是位置(locat ion)。

在這樣的架構下,文件的讀寫基本過程簡化、抽象成如下的過程:

寫流程

1.client 向mast er發送creat e請求,請求包含文件路徑和文件名。mast er根據文件路徑和文件 名,在名字空間里創建一個對象代表這個文件。

2.client 向3個chunkserver發送要寫入到文件中的數據,每個chunkserver收到數據后,將數據 寫入到本地的文件系統中,寫入成功后,發請求給mast er告知mast er一個chunk寫入成功, 與此同時告知client 寫入成功。

3.mast er收到chunkserver寫入成功后,記錄這個chunk與機器之間的對應關系,也就是chunk 的位置。

4.client 確認3個chunkserver都寫成功后,本次寫入成功。

這個寫流程是一個高度簡化抽象的流程,實際的寫流程是一個非常復雜的流程,要考慮到寫入 類型(即,是隨機寫還是尾部追加),還要考慮并發寫入,后面我們繼續詳細的描述寫流程, 解釋GFS是如何處理不同的寫入類型和并發寫入的。

讀流程

1.應用發起讀操作,指定文件路徑,偏移量(off set )

2.client 根據固定的chunk大小(也即64MB),計算出數據在第幾個chunk上,也就是chunk索 引號(index)

3.client 向mast er發送一個請求,包括文件名和索引號,mast er返回3個副本在哪3臺機器上, 也就是副本位置(location of replica)。

4.client 向其中一個副本的機器發送請求,請求包換塊柄和字節的讀取范圍。 

5.chunkserver根據塊柄和讀取范圍從本地的文件系統讀取數據返回給client

這個讀流程未做太多的簡化和抽象,但實際的讀流程還會做一些優化,這些優化和本文主題關 系不大就不展開了。

寫流程詳述

我們詳細的講一下寫入流程的幾個細節。

1.名字空間管理(namespace management)和鎖?;?locking)

寫入流程需要向主發送creat e這樣請求,來操作保存在主上的名字空間。 如果有多個客戶端同時進行寫入操作,那么這些客戶端也會同時操作向主發送creat e請求。主 在同一時間收到多個請求,通過加鎖的方式,防止多個客戶端同時修改同一個文件的元數據。

2.租約(Lease)

client 需要向3個副本寫入數據,在并發的情況下會有多個client 同時向3個副本寫入數據。GFS 需要一個規則來管理這些數據的寫入。 這個規則簡單來講,每個chunk都只有一個副本來管理多個client 的并發寫入。也就是說,對 于一個chunk,mast er會授予其中一個副本一個租約(lease),具有租約的副本來管理所有要寫 入到這個chunk的數據寫入,這個具有租約的副本稱之為首要副本(primary)。其他的副本稱之 為二級副本(secondary)

3.變更次序(Mutation Order) 

將對文件的寫入(不管是在任意位置的寫入,還是在末尾追加)稱之為變更(Mut at ion)。 Primary管理所有client 的并發請求,讓所有的請求按照一定順序(Order)應用到chunk上。

4.基本變更流程

1).client 詢問mast er哪個chunkserver持有這個chunk的lease,以及其他的副本的位置。如果 沒有副本持有lease,mast er挑選一個副本,通知這副本它持有lease。

2).mast er回復client ,告述客戶端首要副本的位置,和二級副本的位置??突Ф肆凳滓?本,如果首要副本無響應或者回復客戶端它不是首要副本,則客戶端重新聯系主。 

3).客戶端向所有的副本推送數據??突Ф絲梢砸勻我獾乃承蟯撲?。每個chunkserver會緩存這 些數據在緩沖區中。 

4).當所有的副本都回復說已經收到數據后,客戶端發送一個寫入請求(write request)給首 要副本,這個請求里標識著之前寫入的數據。首要副本收到請求后,會給寫入分配一個連續的 編號,首要副本會按照這個編號的順序,將數據寫入到本地磁盤。 

5).首要副本將這個帶有編號寫入請求轉發給其他二級副本,二級副本也會按照編號的順序, 將數據寫入本地,并且回復首要副本數據寫入成功。 

6).當首要副本收到所有二級副本的回復時,說明這次寫入操作成功。 

7).首要副本回復客戶端寫入成功。在任意一個副本上遇到的任意錯誤,都會報告給客戶端失敗。

前面講的writ e接口就是按照這個基本流程進行的。 下圖描述了這個基本過程。(摘自GFS的論文)

5.跨邊界變更 

如果一次寫入的數據量跨過了一個塊的邊界,那么客戶端會把這次寫入分解成向多個chunk的 多個寫入。

6.原子記錄追加(Atomic Record Appends)

Record Appends在論文中被稱之為原子記錄追加(Atomic Record Appends),這個接口也 遵循基本的變更流程,有一些附加的邏輯:客戶端把數據推送給所有的副本后,客戶端發請求 給首要副本,首要副本收到寫入請求后,會檢查如果把這個record附加在尾部,會不會超出塊 的邊界,如果超過了邊界,它把塊中剩余的空間填充滿(這里填充什么并不重要,后面我們會 解釋這塊),并且讓其他的二級副本做相同的事,再告述客戶端這次寫入應該在下一塊重試。 如果記錄適合塊剩余的空間,則首要副本把記錄追加尾部,并且告述其他二級副本寫入數據在 同樣的位置,最后通知客戶端操作成功。

原子性

講完架構和讀寫流程,我們開始分析GFS的一致性,首先從原子性開始分析。

Write和Atomic Record Append的區別 前面講過,如果一次寫入的數量跨越了塊的邊界,那么會分解成多個操作,writ e和record append在處理數據跨越邊界時的行為是不同的。我們舉例2個例子來說明一下。

例子1,文件目前有2個chunk,分別是chunk1, chunk2。

Client 1要在54MB的位置寫入20MB數據。這寫入跨越了邊界,要分解成2個操作,第一個操 作寫入chunk1最后10 MB,第二個操作寫入chunk2的開頭10MB。Client 2也要在54MB的位置寫入20MB的數據。這個寫入也跨越邊界,也要分解為2個操作, 作為第三個操作寫入chunk1最后10 MB,作為第四個操作寫入chunk2的開頭10MB。

2個客戶端并發寫入數據,那么第一個操作和第三個操作在chunk1上就是并發執行的,第二個 操作和第四個操作在chunk2上并發執行,如果chunk1的先執行第一操作再執行第三個操作, chunk2先執行第四個操作再執行第二個操作,那么最后,在chunk1上會保留client 1的寫入的 數據,在chunk2上保留了client 2的寫入的數據。雖然client 1和client 2的寫入都成功了,但最 后既不是client 1想要的結果,也不是client 2想要的結果。最后的結果是client 1和client 2寫入 的混合。對于client 1和client 2來說,他們操作都不是原子的。

例子2,文件目前有2個chunk,分別是chunk1, chunk2。

Client 要在54MB的位置寫入20MB數據。這寫入跨越了邊界,要分解成2個操作,第一個操作 寫入chunk1最后10 MB,第二個操作寫入chunk2的開頭10MB。chunk1執行第一個操作成功 了,chunk2執行第二個操作失敗了,也就是寫入的這部分數據,一部分是成功的,一部分是 失敗的,這也不是原子操作。

接下來看record append。由于record append最多能寫入16MB的數據,并且當寫入的數據量 超過塊的剩余空間時,剩余的空間會被填充,這次寫入操作會在下個塊重試,這2點保證了 record append操作只會在一個塊上生效。這樣就避免了跨越邊界分解成多個操作,從而也就 避免了,寫入的數據一部分成功一部分失敗,也避免了并發寫入數據混合在一起,這2種非原 子性的行為。

GFS原子性的含義 

所以,GFS的原子性不是指多副本之間原子性,而是指發生在多chunk上的多個操作的的原子性??梢緣貿穌庋耐坡?,如果Writ e操作不跨越邊界,那么沒有跨越邊界的writ e操作也滿足GFS 所說的原子性。

GFS多副本不具有原子性 

GFS一個chunk的副本之間是不具有原子性的,不具有原子性的副本復制,它的行為是:

一個寫入操作,如果成功,他在所有的副本上都成功,如果失敗,則有可能是一部分副本 成功,而另外一部分失敗。

在這樣的行為如下,失敗是這樣處理的:

  • 如果是write失敗,那么客戶端可以重試,直到write成功,達到一致的狀態。但是如果在 重試達到成功以前出現宕機,那么就變成了永久的不一致了。
  • Record Append在寫入失敗后,也會重試,但是與writ e的重試不同,不是在原有的off set 上重試,而是接在失敗的記錄后面重試,這樣Record Append留下的不一致是永久的不一 致,并且還會有重復問題,如果一條記錄在一部分副本上成功,在另外一部分副本上失 敗,那么這次Record Append就會報告給客戶端失敗,并且讓客戶端重試,如果重試后成 功,那么在某些副本上,這條記錄就會成功的寫入2次。

我們可以得出,Record Append保證是至少一次的原子操作(at least once atomic)。

一致性

GFS把自己的一致性稱為松弛的一致性模型(relaxed consistency model)。這個模型分析元 數據的一致性和文件數據的一致性,松弛主要是指文件數據具有松弛的一致性。

元數據的一致性
元數據的操作都是由單一的mast er處理的,并且操作通過鎖?;?,所以是保證原子的,也保 證正確性的。

文件數據的一致性 

在說明松弛一致性模型之前,我們先看看這個模型中的2個概念。對于一個文件中的區域:

  • 如果無論從哪個副本讀取,所有的客戶端都能總是看到相同的數據,那么就叫一致的 (consist ent );
  • 在一次數據變更后,這個文件的區域是一致的,并且客戶端可以看到這次數據變更寫入的 所有數據,那么就叫界定的(defined)。

GFS論文中,用下面的這個表格總結了松弛一致性:

WriteRecord Append
Serial successdefineddefined interspersed with inconsistent
Concurrent successesconsistent but undefined
Failureinconsistent

分別說明表中的幾種情況:

1.在沒有并發的情況下,寫入不會有相互干擾,成功的寫入是界定的,那么必然也就是一致的 

2.在有并發的情況下,成功的寫入是一致的,但不是界定的,也就是我們前面所舉的例子2。 

3.如果寫入失敗,那么副本之間就會出現不一致。

4.Record Append能夠保證是界定的,但是在界定的區域之間夾雜著一些不一致的區域。 Record Append會填充數據,不管每個副本是否填充相同的數據,這部分區域都會認為是 inconsist ent 的。

如何適應松弛的一致性模型

這種松弛的一致性模型,實際上是一種不能保證一致性的模型,或者更準確的說是一致性的數 據中間夾雜不一致數據。

這些夾雜其中的不一致數據,對應用來說是不可接受的。在這種一致性下,應該如何使用GFS 那?GFS的論文中,給出了這樣幾條使用GFS的建議:依賴追加(append)而不是依賴覆蓋 (overwrite),設立檢查點(checkpointing),寫入自校驗(write self-validating),自記錄標識 (self-identifying records)。

使用方式1:只有單個寫入的情況下,按從頭到尾的方式生成文件。 

  • 方法1.1:先臨時寫入一個文件,再全部數據寫入成功后,將文件改名成一個永久的名 字,文件的讀取方只能通過永久的文件名訪問到這個文件。 
  • 方法1.2:寫入方按一定的周期,寫入數據,寫入成功后,記錄一個寫入進度檢查點 (checkpoint ),這個檢查點包括應用級的校驗數(checksum)。讀取方只去校驗、處理檢查點之前的數據。即便寫入方出現宕機的情況,重啟后的寫入方或者新的寫入方,會 從檢查點開始,繼續重新寫入數據,這樣就修復了不一致的數據。

使用方式2:多個寫入并發向一個文件尾部追加,這個使用方式就像是一個生產消費型的消息 隊列,多個生產者向一個文件尾部追加消息,消費者從文件讀取消息

方法2.1:使用record append接口,保證數據至少被成功的寫入一次。但是應用需要應對 不一致數據,和重復數據。

  • 為了校驗不一致數據,給每個記錄添加校驗數(checksum),讀取方通過校驗數識別 出不一致的數據,并且丟棄不一致的數據。
  • 如果應用讀取數據沒有冪等處理,那么應用就需要過濾掉重復數據。寫入方寫入記錄時 額外寫入一個唯一的標識(ident ifier),讀取方讀取數據后通過標識辨別之前是否已經 處理過這個標識的數據。

GFS的設計哲學

可以看出基于GFS的應用需要通過一些特殊的手段來應對GFS松弛的一致性模型帶來的各種問 題。GFS的一致性保證對于使用者是非常不友好的,很多人第一次看到這樣的一致性保證都是 比較吃驚的。

那么GFS為什么要設計這樣的一致性模型那?GFS在架構上選擇這樣的設計有它自己的設計哲 學。GFS追求的是簡單、夠用的原則。GFS主要要解決的問題是如何使用廉價的服務器存儲海 量的數據,并且達到非常高的吞吐量(GFS非常好的做到了這2點,但不是本文的主題,這里 就不展開了),并且文件系統本身的實現要簡單,能夠快速的實現出來(GFS的開發者在開發 完GFS之后,很快就去開發BigT able了)。GFS很好的完成了完成了這樣的目標。但是留下了 一致性問題,給使用者帶來的負擔。但是在GFS應用的前期,一致性不是問題,GFS的主要使 用者(BigT able)就是GFS開發者,他們深知應該如何使用GFS,這種不一致在BigT able中被 很好屏蔽掉(采用上面所說的方法),BigT able提供了很好的一致性保證。

但是隨著GFS使用的不斷深入,GFS簡單夠用的架構開始帶來很多問題,一致性問題僅僅是其 中的一個。主導了GFS很?時間的Leader Sean Quinlan在一次采訪中,詳細說明了在GFS度 過了應用的初期之后,這種簡單的架構帶來的各種問題[1]。

開源分布式文件系統HDFS,清晰的看到了GFS的一致性模型給使用者帶來的不方便,堅定地 摒棄了GFS的一致性模型,提供了很好的一致性保證。HDFS達到了怎樣的一致性,這里就不 在展開了,另外再做詳細的討論。

總之,保證一直性對于分布式文件系統,甚至所有的分布式系統都是很重要的。

1.GFS: Evolution on Fast-forward, Sean Quinlan, 2009, 

https://queue.acm.org/detail.cf m?id=1

原文鏈接:https://mp.weixin.qq.com/s/GuJ6VqZJy3ONaVOWvQT9kg

最干貨的java+分布式技術公眾號,兼及研發管理。本號專家陣容:螞蟻金服右軍、易寶CTO陳斌、米么金服總監李偉山、奧琪金科首席架構曲健、螞蟻金服高級技術專家張翔、美團高級技術專家楊彪等。

企業分支的數字化轉型

上一篇

本地讀寫的多活數據存儲架構設計要義

下一篇

你也可能喜歡

從 GFS 失敗的架構設計來看一致性的重要性

長按儲存圖像,分享給朋友

ITPUB 每周精要將以郵件的形式發放至您的郵箱


微信掃一掃

微信掃一掃