海量數據怎么存?HDFS 是什么?架構是怎么樣的?
你是一個程序員,你太想進步了,于是準備下載 256g 的學習資料,放到電腦硬盤上。
但你的電腦最多只能存放 128g 數據。怎么辦呢?
好辦,你衣柜里還有臺大學時的舊電腦。平分一下,正好夠放。下崗機器再就業,你感覺你是個天才。
可麻繩總挑細處斷,舊電腦磁盤終究還是寫壞了。
于是你選擇從衣柜里再拿兩臺舊電腦做備份。這樣就不怕磁盤壞了。
但這個切分數據和備份數據的過程每次都得手動操作。
手動切分和備份數據
不僅容易出錯,還賊浪費時間!
有解法嗎?
有,沒有什么是加一層中間層不能解決的,如果有,那就再加一層。
這次我們要加的中間層是 HDFS。
hdfs
HDFS 是什么?
HDFS, 全名 Hadoop Distributed File System,是大數據領域常用的分布式文件系統。
你可以將它當做 應用服務和多個服務器文件系統的中間層,幫應用屏蔽掉背后的多個服務器,從多個服務器上讀寫文件數據。
并通過一系列策略保證數據可靠性,就算某些服務器磁盤壞了,也不影響數據完整。
hdfs是應用服務和多個服務器文件系統的中間層
我們來看下它是怎么做到的。
數據塊是什么
如果你有個超大文件,該怎么將它們存到多臺服務器磁盤上呢?
按理說直接挑一臺磁盤充足的服務器寫入就好了,但如果每臺服務器的剩余磁盤空間都不足以存下這個大文件呢?
好辦, 我們可以將大文件切成多個數據塊,也就是 block, 每個數據塊默認 128MB。
數據塊
這個大小的數據塊正好可以寫入到多個服務器磁盤的犄角旮旯里,既完成了大文件的存儲,又提升了磁盤空間利用率。
容錯
但如果這時候某臺服務器磁盤被寫壞了,那背后牽連的很多大文件,就全廢了。怎么辦呢?
追愛路上遍體鱗傷的沸羊羊,會含淚給你答案,當然是多養幾個備胎。
我們可以將數據塊復制幾份出來,分散放到不同服務器上,就算其中一臺服務器跪了,還能從其他服務器上拿到數據塊。分散了風險,大大提升了系統容錯率!
在其他服務器上冗余數據
但問題又來了,大文件被拆成了多數據塊,多副本寫入后。如果程序想讀大文件,怎么知道該從哪個服務器里讀呢?
HDFS 架構
為了解決上面的問題,HDFS 會將我們的服務器集群劃為兩部分,一部分是Master 節點,也叫 NameNode,另一部分是Slave 節點,也叫DataNode。
hdfs架構
從名字能看出,它們的關系就是老板和打工人。
NameNode 負責管理 DataNode,決定應用程序該到哪個 DataNode 去讀寫數據塊。DataNode 才是真正負責存儲數據塊的牛馬。
它們共同構成了 HDFS 集群,對外提供了讀寫文件,以及修改讀寫權限等一系列能力。
hdfs集群
而且 HDFS 還提供了 CLI 和 API,程序員可以方便地進行文件操作,不需要手寫代碼來處理大文件的拆分和組裝。
可以通過CLI和API訪問hdfs
DataNode 是什么
牛馬 DataNode 負責實際存儲數據,很辛苦,但它的工作確實沒什么技術含量,寫壞了就用另外一塊新的 DataNode 頂上。主打一個你不干,有的是 DataNode 愿意干。存儲的數據量大了,就多加幾個 DataNode。
正因為 DataNode 每天都需要瘋狂讀寫,所以身體,啊不對,磁盤很容易垮,但是其他 DataNode 上面也備份了文件數據,可替代性很高,所以不用給它們配太好的服務器,能跑就行。
datanode使用普通服務器
反觀 NameNode,就不一樣了,它維護了所有服務器集群的信息,是大腦,是核心,金貴的很,所以得用高性能服務器好生供養著。
不行,越說越生氣了。
namenode使用高性能服務器
我們看下 NameNode 是怎么管理文件的?
NameNode 是什么
我們平時在電腦上,是通過目錄樹的形式管理文件。
而在 HDFS 的 NameNode 中,也用類似的目錄樹形式管理文件,每個文件都有對應文件名、大小和對應地址以及訪問權限。這些信息,我們叫它元數據。
元數據
這個管理目錄樹和元數據的能力,就叫 NameSpace。
同時 NameNode 還記錄了某個文件,分成了多少個數據塊這些信息。知道了大文件有哪些數據塊后,我們還需要維護和管理數據塊被存在了哪個 DataNode 上,這部分能力叫 Block Manager。
NameNode內部
高性能
為了支持高性能讀寫,NameNode 將 NameSpace 和 Block Manager 的數據全放內存中。
NameNode將數據放內存
持久化
但放內存里有個大問題,進程要是崩了,那數據就丟了。
怎么辦呢?
我們可以將 NameSpace 和 Block Manager 定期持久化到磁盤文件里,這個文件就是 fsimage,它記錄了某一時刻 NameNode 的全量數據,類似于游戲的"存檔"。
NameNode數據存檔
但"存檔"是需要時間的,在這次存檔完成之后,下一次存檔完成之前,寫入的數據是不是會丟失呢?
好辦,NameNode 會將"存檔"后寫入了哪些信息,記錄到一個叫 editlog 的文件里,定時刷盤。這樣就算進程掛了,重啟的時候,通過加載 fsimage+editlog, 就能盡可能復原數據。保證了數據可靠性。
引入editlog
高可用
想必大家也發現了,NameNode 是 HDFS 集群的核心,存在單點問題,要是崩了,那集群就沒法對外提供服務了。
所以為了保證高可用,我們可以為 NameNode 配一個備用 NameNode, 也就是 Standby NameNode,平時主 NameNode 負責對外提供讀寫操作,備用 NameNode 只同步 NameNode 的數據。
一旦 NameNode 掛了,備用 NameNode 就能立馬頂上。保證了集群高可用。
備用namenode
可擴展
但就算用了備用 NameNode,同一時刻,集群里其實只有一個 NameNode 對外工作。
隨著 HDFS 集群規模變大,NameNode 使用的內存也會變高。換句話說就是, HDFS 性能其實受限于單服務器節點的內存和 cpu 上限。那有辦法擴展嗎?
有!我們知道 NameNode 里的 NameSpace 本質上是個目錄樹。
目錄樹
為了水平擴展,我們可以根據業務屬性,對目錄樹進行拆分,也就是變成多個 NameSpace。
根據業務拆分目錄樹
再新增 NameNode,每個 NameNode 各自維護一個獨立的 NameSpace,NameNode 之間完全不需要知道對方存了哪些數據,各自都只需要根據 DataNode 當前上報的磁盤信息就能完成讀寫操作。
通過這個方式,降低了 NameNode 單節點壓力,同時提升了系統擴展性。這其實就是業界比較經典的 HDFS Federation 方案。
HDFS Federation方案
但單個 NameSpace 還是有可能變得很大,怎么辦呢?
好辦,單個 NameSpace 過大并不合理,再拆小就行了。
這就很靈性了,在你質疑我擴展性有問題之前,我先反過來質疑你業務耦合過大,是不是能拆一下。在我解決架構問題之前,先解決掉提出問題的人,也不失為一種優雅的架構師思維。
接下來我們將上面提到的內容串起來。
寫大文件
- 客戶端通過 HDFS API 向 NameNode 發送請求,準備寫入文件。
- NameNode 在 NameSpace 中檢查文件路徑的合法性和客戶端寫權限,ok 的話,NameNode 會在 Edit Log 中記錄新文件的元數據(比如文件路徑、權限等),再更新 NameSpace。然后,NameNode 響應客戶端。
- 之后客戶端再請求 NameNode ,獲取第一個數據塊寫入到哪些個 DataNode 上。
寫入流程part1
- NameNode 的Block Manager 會根據當前存儲情況,告訴客戶端數據塊應該存儲在哪些 DataNode 上。
- 客戶端將數據塊先寫入主DataNode,DataNode 再將數據塊副本同步寫到其他 DataNode 上。
- 完成第一個數據塊后。客戶端通知 NameNode 數據塊已成功寫入。NameNode 更新數據塊的時間戳,最終大小等。再寫入 editlog。
寫入流程part2
- 客戶端再向 NameNode 獲取第二個數據塊該寫到哪些個 NameNode 上,重復上面的操作,直到全部寫完。
- DataNode 將數據塊存儲在本地文件系統后,會定期向 NameNode 匯報數據塊狀態。
datanode定期上報狀態
- NameNode 將文件的元數據變化記錄到 NameSpace EditLog 中。并定期合并 EditLog 到 FsImage,以確保文件系統狀態一致。
- 備用 NameNode 會同步 EditLog 和 FsImage,以便在故障時可以接管。
備用namenode
通過上面步驟,HDFS 完成寫入大文件。我們再來看下怎么將大文件讀出來。
讀大文件
- 客戶端向 NameNode 發送請求以獲取目標文件的元數據信息。NameNode 返回文件的 block 列表及其對應的 DataNode 位置。
- 客戶端根據 NameNode 返回的 DataNode 列表,選擇一個合適的 DataNode ,建立 TCP 連接,并發送讀取 block 的請求。
- DataNode 收到請求后,將 block 數據發給客戶端。客戶端接收數據后,用 checksum 校驗數據完整性。
- 重復以上步驟讀取到多個數據塊后,將多個數據塊組裝成大文件,完成讀取。
讀大文件
現在大家通了嗎?
最后
其實大部分后端開發平時不怎么使用 HDFS,但我卻不得不聊下它,它是大數據體系的基石。基于 HDFS 的中間件有很多,比如 Hbase, Hive, Spark 等等,隨便拉出一個來,都是王炸。就算不用,我們也可以學習下它們是怎么解決架構問題的。這在面試上拿出來吹牛,還不是嘎嘎亂殺?
總結
- 你可以將 HDFS 當做 應用服務和多個服務器文件系統的中間層,幫應用屏蔽掉背后的多個服務器,從多個服務器上讀寫文件數據,
- HDFS 會將文件分為多個數據塊,并給數據塊配備多個副本,有效利用磁盤空間的同時,還提升了數據可靠性。
- HDFS 將集群分為 NameNode 和 DataNode 兩部分,NameNode 負責管理文件元數據,DataNode 負責正在存儲數據塊。
- NameNode 的內存中主要包含 NameSpace 和 block manager 兩部分,NameSpace 負責管理目錄樹和元數據的組件。Block manager 維護和管理數據塊被存在了哪個 DataNode 上。這些內存數據會刷入磁盤上的 fsimage 文件中形成快照,并通過 editlog 記錄用戶寫操作,確保數據不丟
- NameNode 將 NameSpace 和 block manager 加載到內存中,保證高性能。同時將內存數據會刷入磁盤上的 fsimage 文件中形成快照,并通過 editlog 記錄用戶寫操作,確保數據持久化。為 NameNode 加入備用節點,保證高可用。通過 Federation 方案,提升了系統擴展能力。