分散式儲存系統的難點
在儲存系統中,為了獲得巨大的效能加成,一個很自然的想法就是採用分片(sharding),將資料分割儲存到多臺伺服器上,這樣獲得了更大的儲存容量,而且可以並行地從多臺伺服器讀取資料。
我們在成百上千臺伺服器上進行分片,大量基數的情況下,出現錯誤的頻率也大大提升,我們需要一個自動化的從錯誤中恢復的方法,這就引入了容錯(fault tolerance)。
實現容錯的最有用的方法就是複製(replication),使用副本儲存相同的資料。
有了副本,就需要考慮副本之間的不一致性(inconsistency)問題。
為了解決不一致性的問題,需要進行一定的設計,使各個副本之間保持一致,這需要網路通訊來令伺服器互動。因此,一致性的代價就是低效能。
這是一個迴圈,我們需要進行一定的權衡和犧牲。
GFS的設計目標
- 系統必須將故障視為一種常態,能夠迅速偵測、冗餘並恢復失效的元件;
- 系統需要支援大檔案(數GB),並且能夠有效地進行管理;
- 系統的工作負載主要是兩種讀操作:大規模的流式讀取和小規模的隨機讀取,前者來自同一個客戶機的連續操作讀取同一個檔案的連續區域,後者通常是在檔案某個隨機位置讀取幾個KB,通常後者會被合併並排序按順序批量讀取,以此提高效能;
- 系統的寫工作負載時大規模的、順序的資料追加方式的寫操作,資料一旦被寫入之後檔案就很少會被修改了,同時系統也要支援小規模的隨機位置寫入;
- 支援多客戶端並行追加資料到同一個檔案;
- 關注批處理的效能,而非系統的響應速度。
GFS設計概述
GFS有一個master節點(邏輯上的一個),多個chunkserver,為client提供服務。檔案被分割成固定大小的chunk,chunk建立的時候,master伺服器會給每個chunk分配一個不變的、全球唯一標識的64位chunk標識,chunk伺服器把chunk以linux檔案的形式儲存在本地的硬碟上,並且根據指定的chunk標識和位元組範圍來讀寫資料。出於可靠性考慮,每個chunk可能會被複制到多個chunkserver上。
master節點管理所有的檔案系統後設資料,但不儲存檔案資料(這使得GFS將檔案系統管理和資料儲存分開了),這些後設資料包括名字空間、訪問控制資訊、檔案和chunk的對映資訊、當前chunk的位置資訊。master節點還管理著系統範圍內的活動,比如chunk租用、孤兒chunk的回收、以及chunk在chunkserver之間的遷移。master節點用心跳資訊週期地和每個chunkserver通訊,傳送指令到各個chunkserver並接收chunkserver的狀態資訊。
GFS客戶端程式碼以庫的形式被連結到client的客戶程式裡,使client可以通過這些函式進入GFS系統訪問資料。
一次讀取的流程是,首先client將檔名和客戶程式指定的位元組偏移,根據固定的chunk大小轉換成檔案的chunk索引,然後把檔名和chunk索引傳送給master節點,master節點將相應的chunk表示和副本的位置資訊傳送回client,client用檔名和chunk索引作為key來快取這些資訊。之後客戶端傳送請求到其中一個最近的副本處(根據ip計算距離),請求資訊包括了chunk的標識和位元組範圍。
tips:單一的master節點雖然大大簡化了設計,但也可能導致master節點成為系統的效能瓶頸所在。因此我們必須儘量減少master節點的讀寫。
客戶端將從master獲取的後設資料快取一段時間,後續的操作將直接和chunkserver進行資料讀寫,除非後設資料資訊過期或者檔案被client重新開啟。
一些討論
chunk尺寸
chunk的大小是關鍵的設計引數之一,通常選用64MB,這個尺寸遠遠大於一般檔案系統的blocksize(惰性分配可以避免因為大的chunk尺寸造成的內部碎片)。這樣做有許多優點:1. 減少了client和master的通訊,因為只需要一次和master節點的通訊就可以獲取chunk的位置資訊,之後就可以對同一個chunk進行多次的讀寫操作;2. 採用較大的chunk尺寸時,client能夠對一個塊進行多次操作,通過與chunkserver保持較長時間的tcp連線來降低網路負載;3. 較大的chunk尺寸減少了對master儲存能力的要求,同時後設資料也更有可能全部放在master的記憶體中,這樣訪問速度會快很多。
大的chunk尺寸也有一定的缺陷:這通常會產生熱點問題。小的檔案包含較少的chunk,如果多個client同時對小檔案進行訪問,儲存這些chunk的chunkserver會成為熱點。也許多個client同時訪問一個小檔案並不是GFS設計目標中的常見情景,但是當GFS初次啟動的時候,一個可執行檔案在GFS上儲存為singe-chunk檔案,之後這個可執行檔案在數百臺機器上同時啟動,此時熱點問題就出現了。為了解決熱點問題,通常使用更大的複製引數來儲存可執行檔案(在更多的chunkserver上覆制更多份)、或者錯開系統啟動的時間、再或者允許client從其他client讀取資料(這裡就引入了一個新的通訊路徑)。
後設資料
master伺服器主要儲存三種型別的後設資料:檔案和chunk的名稱空間、檔案和chunk的對應關係、每個chunk副本的存放地點。名稱空間和對應關係的更新採用儲存變更日誌的方式更新master伺服器的狀態,日記由作業系統記錄在磁碟上,具有永續性。存放地點不會由master伺服器持久化儲存,master伺服器在啟動或者有新的chunkserver加入時,向chunkserver詢問儲存的chunk資訊,同時定期輪詢更新,以此獲得chunk副本最新的存放地點。
tips:master始終通過chunkserver獲取chunk的存放地點,這一設計源於一個思想:只有chunkserver才能最終確定一個chunk是否在它的硬碟上。例如,chunkservre的故障可能會導致chunk的消失,而這是master無法自動獲知的。
一致性
這裡的一致性是指chunk副本之間的一致性,要求所有chunk副本中儲存的內容都是已定義的,並且包含最後以此修改操作寫入的資料。為了達到這樣的一致性,GFS有以下兩項措施:對chunk的所有副本的修改操作順序一致、使用chunk的版本號來檢測副本是否因為它所在的chunk伺服器當機而錯過了修改操作導致其失效。GFS通過master伺服器和所有chunkserver的定期握手來找到失效的chunkserver,並且使用checksum來校驗資料是否損壞。一旦發現問題,資料要儘快利用有效的副本進行恢復。
系統互動
tips:設計系統時,一個重要原則時最小化所有操作和master節點之間的互動。
使用租約(lease)對chunk的多個副本進行主從管理,master節點為chunk的一個副本建立租約,把這個副本叫做主chunk,主chunk對chunk的所有更改操作進行序列化,所有的副本都遵從這個序列進行修改。
- 客戶機向master節點詢問哪一個chunkserver持有當前的租約,以及其他副本的位置(如果沒有任何一個持有租約,那麼master建立一個);
- master將主chunk的識別符號和其他副本(也叫secondary副本)的位置返回給客戶機,client快取這些資料,只有當主chunk不可用,或者主chunk回覆資訊表明它已經不再持有租約的時候,client才需要重新跟master節點聯絡;
- client推送資料到所有的副本上(可以以任意順序推送)(tips:chunkserver用LRU快取這些資料; 此處網路IO負載非常高,需要合理規劃網路拓撲資料流);
- 當所有副本都確認接收了資料,client傳送寫請求到主chunkserver(這個請求標識了之前推送到副本的資料),主chunkserver為所有操作分配連續的序列號,在本地執行。
- 主chunkserver將寫請求傳遞給所有二級副本,二級副本以相同的順序執行這些操作;
- 二級副本完成操作後會回覆主chunkserver。
- 主chunkserver回覆client,如果失敗或者部分失敗,client將重新執行3~7步。