The Google File System 論文筆記

在閱讀本論文時,我還閱讀了 OceanBase 的大佬寫的書[1],配合論文閱讀會更加有收穫。這本書雖是13年寫的,但對於當時許多主流的技術均有涉及(不過有羅列的嫌疑),做者本人的功力也是很是深厚的。node

在論文簡介中給出了 GFS 的 4個決策依據(第4個感受不是假設,而是結果):數據庫

  • 組件的失敗是頻繁的。包括磁盤、內存、網絡、系統,甚至人爲因素。計算機是不可信的。
  • 文件系統中的文件很大(GB 以上)。這一點決定了以後的一些設計決策,好比塊的大小。對於谷歌而言,這是很是天然的,由於谷歌的搜索依賴於倒排索引,數量很是巨大。不過,這個假設時至今日已經再也不有力,因此不管是谷歌仍是阿里都有本身支持小文件的分佈式文件系統。
  • 大部分文件都是追加而非修改操做。不存在隨機的數據寫入。這個假設熟悉 HDFS 的人應該都不陌生。這其實是數據倉庫的基本條件之一。
  • 應用程序和文件系統 API 的協同設計提升了整個系統的靈活性。這個指的是 GFS 使用了比較弱的一致性模型,因此應用程序不須要作不少複雜的操做好比加鎖。

架構

GFS 的架構很簡單,但它不是一般意義上的主從結構,而是 master 和 chunkserver,也就是存儲元數據和文件數據的兩種服務器。這裏雖然論文中用了 single 這個詞,但 master 是有 replication 機制的。緩存

The master maintains all file system metadata. This includes the namespace, access control information, the mapping from files to chunks, and the current locations of chunks. It also controls system-wide activities such as chunklease management, garbage collection of orphaned chunks, and chunkmigration between chunkservers. The master periodically communicates with each chunkserver in HeartBeat messages to give it instructions and collect its state.
GFS client code linked into each application implements the file system API and communicates with the master and chunkservers to read or write data on behalf of the application. Clients interact with the master for metadata operations, but all data-bearing communication goes directly to the chunkservers.
Neither the client nor the chunkserver caches file data. Client caches offer little benefit because most applications stream through huge files or have working sets too large to be cached. Not having them simplifies the client and the overall system by eliminating cache coherence issues. (Clients do cache metadata, however.) Chunkservers need not cache file data because chunks are stored as local files and so Linux’s buffer cache already keeps frequently accessed data in memory.安全

GFS 的單點 master 決定了它必須減小 master 的負載,所以客戶端在獲得了 元數據信息以後,會進行緩存,從而避免和 master 通訊。另外,客戶端在獲取元信息時也會盡量多的獲取 chunk 的信息,這樣就減小了重複通訊。服務器

master 給每一個 chunkserver 一個惟一的標識符,這個在 hdfs 裏就是 clusterId,能夠在dfs.namenode.data.dir 配置的目錄下的 current/version 裏找到。網絡

一個大體的讀流程是這樣的:首先,客戶端根據固定 的 Chunk 大小把文件名和程序指定的字節偏移轉換成文件的 Chunk 索引。而後它把文件名和 Chunk 索引起送給 Master 節點。Master 節點將相應的 Chunk 標識和副本的位置信息發給客戶端,客戶端用文件名和 Chunk 索引做爲 key 緩存這些信息。多線程

在實際中,咱們所謂的客戶端(好比說筆記本)不太可能直連集羣,由於咱們實際上會訪問諸多數據節點,這裏面除了網絡通訊的問題,還有安全問題。架構

能夠看到,這裏最小化了 master 的開銷。併發

Chunk Size

在 GFS 中設計的分塊大小是 64MB(Hadoop 也效仿了,後來改爲了 128MB)。爲何要選擇一個大於文件塊大小的分塊呢?主要有兩個緣由:app

  • 元數據信息變小。這能夠保證 master 能夠將全部元數據載入內存,同時客戶端能夠緩存更多的元信息;
  • 減小通訊。因爲前面假設存儲的文件一般是大文件,所以能夠保證儘量多地讀寫同一個塊的內容。

可是,更大的塊會形成數據的熱點問題。但這主要是由於小文件的密集訪問致使的(大文件咱們能夠錯開各個塊的訪問)。

Meta Data

元數據分爲3類:文件和 Chunk 的命名空間、文件和 Chunk 的對應關係、每一個 Chunk 副本的存放地點。這些元數據是保存在內存裏的,前兩者也會存儲它們的變動日誌。爲何副本的位置不須要呢?仍是由於組件是很容易崩潰的,所以 GFS 採用輪詢來獲取這個信息(也就是 hdfs 的 BlockReport),而沒有進行持久化。

實際上,論文中也指出,他們在一開始也嘗試過持久化 chunk 的位置信息,可是並不理想。顯然,持久化意味着一致性問題,在 chunkserver 上下線甚至改名的時候,都是很麻煩的。不過,因爲 master 沒有一致性的(全局)視圖,因此咱們有可能在讀寫數據時出錯,這時就須要實現一些容錯的機制。

Operation Log

這個操做日誌的設計就不用說了,基本上就是數據庫裏的老生常談了,包括 CheckPoint,分組提交等等。Checkpoint 文件以壓縮B-樹存儲,能夠直接映射到內存中。

按照論文的說法,諸如 CheckPoint 的生成和日誌的切換都是在一個新的線程裏完成的,實際上就是 hdfs 的 Secondary Name Node。這個節點一般會被放在另外一個服務器中,從而完全的避免對 master 的delay。

Consistency Model

一致性是 GFS 的重頭戲之一。能夠參考論文給出的一幅圖:

這裏的 file region 應該指的是一個文件中的某個內容範圍。若是一個 region 對於全部的客戶端都是一致的,那麼就認爲是圖中的 consistent;而 defined 的要求更高,不只是 consistent,並且必需要保證寫入有效(客戶端能夠看到全部寫入的內容),但在併發寫入下可能會被覆蓋。

GFS,一致性模型裏,「已定義」和「不一致」具體表示的什麼含義?

若是失敗就會不一致,由於可能存在部分節點已寫的狀況。但爲何併發追加成功了也會形成部分的不一致呢?而爲何併發寫沒有這個問題呢?

Data mutations may be writes or record appends. A write causes data to be written at an application-specified file offset. A record append causes data (the 「record」) to be appended atomically at least once even in the presence of concurrent mutations, but at an offset of GFS’s choosing (Section 3.3).

這裏出現了一個分佈式系統的術語——至少一次。也就是說,這裏的成功是存在重試的,由於 chunkserver 可能存在暫時的不可用。在追加模式下,master 可能會從新分配新的 region,這樣就形成了各個副本之間並非徹底一致。但因爲能夠經過校驗和來拋棄重複數據,所以至少一次並無太大的影響——即便 reader 端會所以產生非冪等操做,也能夠經過增長惟一標識的方式來避免。

能夠看到,GFS 沒有采用 2PC 來進行多副本的複製,這意味着各個副本可能存在不一致性。但能夠經過客戶端重試來解決這個問題。

System Interactions

Leases and Mutation Order

這一部分在 Hadoop 裏實現差別比較大。在 GFS 中,租約是一種針對每一個 chunk 的委託,將多副本協調的權力交給了其中一個副本,從而減小 master 的壓力。租約其實是 master 上的一種鎖。持有租約的 chunkserver 會序列化對 chunk 的修改操做來保證次序。但在 HDFS 裏,租約是頒給客戶端的,它是客戶端寫和建立文件的憑據。固然,它也是一種鎖。

顯然,若是客戶端獨佔了該文件,那麼就不存在併發寫入的問題了。Hadoop 的一大特色就是不支持併發寫(不管是追加仍是隨機寫)。GFS 後來的覆盤也說明,這是一個英明的決定[2]

Was this done by design?

At the time, it must have seemed like a good idea, but in retrospect I think the consensus is that it proved to be more painful than it was worth. It just doesn't meet the expectations people have of a file system, so they end up getting surprised. Then they had to figure out work-arounds.

In retrospect, how would you handle this differently?

I think it makes more sense to have a single writer per file.

decouple the flow

GFS 分離了數據流和控制流。但在 HDFS 裏,咱們知道控制信息和數據都是經過某個離客戶端較近的 datanode 發送的。但數據的 pipeline 是相同的,由於這能夠最大化網絡傳輸的效率。

Atomic Record Appends

由於 HDFS 不支持併發寫,因此這個特性對於 hdfs 就不是必要的了。這個原子並非傳統意義上幾個機器之間的同步,而指的是寫入操做自己不能被再分塊[3]。這是有可能的,由於咱們可能但願在一個文件寫入的位置和長度超過了這個 chunk。此時 GFS 會經過填充 padding 來處理,也就是說,強制從一個新的 chunk 開始來保證寫入是原子的。

Snapshot

這個使用 COW 的快照機制和咱們想的差很少,就是針對當前快照的目錄或者文件作標記,從而在下次寫入請求以前進行重定向,就地(指的是在 chunkserver 上)建立一個新的副本。惟一須要注意的是,master 在收到快照請求後,它會當即取消快照對應文件的全部租約。這樣就報證了以後全部的寫均可以被 master 先收到。

Master

Namespace Management and Locking

GFS 經過給命名空間增長讀寫鎖來保證多線程併發不受干擾。惟一比較 trick 的地方是,這裏只須要獲取父目錄的讀鎖,就能夠保證父目錄不被刪除或者更名。

Garbage Collection

GFS 的垃圾回收是惰性的。首先,它隱藏而不是直接刪除元數據;其次,它不發送刪除消息(由於可能會丟失),而是等待握手時將 chunk 失效的信息發送出去。延遲迴收的缺點是不能得到即時的空間調優,但 GFS 論文裏也提到,能夠經過設置命名空間不一樣的回收策略來達到優化的目的,或者顯式的再次刪除一個已經刪除的文件。

Stale Replica Detection

對於過時的副本,GFS 經過版本號來肯定(這有點像 MVCC)。而且在垃圾回收中,能夠移除全部過時的副本。

If the master sees a version number greater than the one in its records, the master assumes that it failed when granting the lease and so takes the higher version to be up-to-date.

不太清楚何時 master 會看到一個比本身高的版本號。難道是 master 宕機的時候?

Fault tolerance

容錯是分佈式系統的核心問題。GFS 在高可用上的措施是很是簡單的,和 MySQL 很是像,也就是 Checkpoint 加上 edit log 的模式。

Master 是具備高可用機制的,存在外部的監控程序來進行 master 的替換:

If its machine or disk fails, monitoring infrastructure outside GFS starts a new master process elsewhere with the replicated operation log. Clients use only the canonical name of the master (e.g. gfs-test), which is a DNS alias that can be changed if the master is relocated to another machine.

但這個機制一直沒有被 hadoop 引入。直到後來 hdfs 纔有了Standby NameNode 的高可用機制(我記得好像是華爲的大佬貢獻的?)。

另外 GFS 裏還提到了 shadow master,它是 master 的只讀副本,至關於真正的主從結構。

References


  1. 大規模分佈式存儲系統:原理解析與架構實戰 ↩︎

  2. GFS: Evolution on Fast-forward ↩︎

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

相關文章
相關標籤/搜索