什麼是HDFS?算了,告訴你也不懂。
前言
只有光頭才能變強。
文字已收錄至我的GitHub精選文章,歡迎Star:https://github.com/ZhongFuCheng3y/3y
上一篇已經講解了「大資料入門」的相關基礎概念和知識了,這篇我們來學學HDFS。如果文章有錯誤的地方,不妨在評論區友善指出~
一、HDFS介紹
上篇文章已經講到了,隨著資料量越來越大,在一臺機器上已經無法儲存所有的資料了,那我們會將這些資料分配到不同的機器來進行儲存,但是這就帶來一個問題:不方便管理和維護
所以,我們就希望有一個系統可以將這些分佈在不同操作伺服器上的資料進行統一管理,這就有了分散式檔案系統
- HDFS是分散式檔案系統的其中一種(目前用得最廣泛的一種)
在使用HDFS的時候是非常簡單的:雖然HDFS是將檔案儲存到不同的機器上,但是我去使用的時候是把這些檔案當做是儲存在一臺機器的方式去使用(背後卻是多臺機器在執行):
- 好比:我呼叫了一個RPC介面,我給他引數,他返回一個response給我。RPC介面做了什麼事其實我都不知道的(可能這個RPC介面又調了其他的RPC介面)-----遮蔽掉實現細節,對使用者友好
明確一下:HDFS就是一個分散式檔案系統,一個檔案系統,我們用它來做什麼?存資料呀。
下面,我們來了解一下HDFS的一些知識,能夠幫我們更好地去「使用」HDFS
二、HDFS學習
從上面我們已經提到了,HDFS作為一個分散式檔案系統,那麼它的資料是儲存在多個系統上的。例如,下面的圖:一個1GB的檔案,會被切分成幾個小的檔案,每個伺服器都會存放一部分。
那肯定會有人會問:那會切分多少個小檔案呢?預設以128MB
的大小來切分,每個128MB
的檔案,在HDFS叫做塊(block)
顯然,這個128MB大小是可配的。如果設定為太小或者太大都不好。如果切分的檔案太小,那一份資料可能分佈到多臺的機器上(定址時間就很慢)。如果切分的檔案太大,那資料傳輸時間的時間就很慢。
PS:老版本預設是64MB
一個使用者發出了一個1GB
的檔案請求給HDFS客戶端,HDFS客戶端會根據配置(現在預設是128MB
),對這個檔案進行切分,所以HDFS客戶端會切分為8個檔案(也叫做block),然後每個伺服器都會儲存這些切分後的檔案(block)。現在我們假設每個伺服器都儲存兩份。
這些存放真實資料的伺服器,在HDFS領域叫做DataNode
現在問題來了,HDFS客戶端按照配置切分完以後,怎麼知道往哪個伺服器(DataNode)放資料呢?這個時候,就需要另一個角色了,管理者(NameNode)。
NameNode實際上就是管理檔案的各種資訊(這種資訊專業點我們叫做MetaData「元資料」),其中包括:文檔案路徑名,每個Block的ID和存放的位置等等。
所以,無論是讀還是寫,HDFS客戶端都會先去找NameNode,通過NameNode得知相應的資訊,再去找DataNode
- 如果是寫操作,HDFS切分完檔案以後,會詢問NameNode應該將這些切分好的block往哪幾臺DataNode上寫。
- 如果是讀操作,HDFS拿到檔名,也會去詢問NameNode應該往哪幾臺DataNode上讀資料。
2.1 HDFS備份
作為一個分散式系統(把大檔案切分為多個小檔案,儲存到不同的機器上),如果沒有備份的話,只要有其中的一臺機器掛了,那就會導致「資料」是不可用狀態的。
寫到這裡,如果看過我的Kafka和ElasticSearch的文章可能就懂了。其實思想都是一樣的。
Kafka對partition備份,ElasticSearch對分片進行備份,而到HDFS就是對Block進行備份。
儘可能將資料備份到不同的機器上,即便某臺機器掛了,那就可以將備份資料拉出來用。
對Kafka和ElasticSearch不瞭解的同學,可以關注我的GitHub,搜尋關鍵字即可查詢(我覺得還算寫得比較通俗易懂的)
注:這裡的備份並不需要HDFS客戶端去寫,只要DataNode之間互相傳遞資料就好了。
2.2 NameNode的一些事
從上面我們可以看到,NameNode是需要處理hdfs客戶端請求的。(因為它是儲存元資料的地方,無論讀寫都需要經過它)。
現在問題就來了,NameNode是怎麼存放元資料的呢?
- 如果NameNode只是把元資料放到記憶體中,那如果NameNode這臺機器重啟了,那元資料就沒了。
- 如果NameNode將每次寫入的資料都儲存到硬碟中,那如果只針對磁碟查詢和修改又會很慢(因為這個是純IO的操作)
說到這裡,又想起了Kafka。Kafka也是將partition寫到磁盤裡邊的,但人家是怎麼寫的?順序IO
NameNode同樣也是做了這個事:修改記憶體中的元資料,然後把修改的資訊append(追加)到一個名為editlog
的檔案上。
由於append是順序IO,所以效率也不會低。現在我們增刪改查都是走記憶體,只不過增刪改的時候往磁碟檔案editlog
裡邊追加一條。這樣我們即便重啟了NameNode,還是可以通過editlog
檔案將元資料恢復。
現在也有個問題:如果NameNode一直長期執行的話,那editlog
檔案應該會越來越大(因為所有的修改元資料資訊都需要在這追加一條)。重啟的時候需要依賴editlog
檔案來恢復資料,如果檔案特別大,那啟動的時候不就特別慢了嗎?
的確是如此的,那HDFS是怎麼做的呢?為了防止editlog
過大,導致在重啟的時候需要較長的時間恢復資料,所以NameNode會有一個記憶體快照,叫做fsimage
說到快照,有沒有想起Redis的RDB!!
這樣一來,重啟的時候只需要載入記憶體快照fsimage
+部分的editlog
就可以了。
想法很美好,現實還需要解決一些事:我什麼時候生成一個記憶體快照fsimage
?我怎麼知道載入哪一部分的editlog
?
問題看起來好像複雜,其實我們就只需要一個定時任務。
如果讓我自己做的話,我可能會想:我們加一份配置,設定個時間就OK了
- 如果
editlog
大到什麼程度或者隔了多長時間,我們就把editlog檔案的資料跟記憶體快照fsiamge
給合併起來。然後生成一個新的fsimage
,把editlog
給清空,覆蓋舊的fsimage
記憶體快照- 這樣一來,NameNode每次重啟的時候,拿到的都是最新的fsimage檔案,editlog裡邊的都是沒合併到fsimage的。根據這兩個檔案就可以恢復最新的元資料資訊了。
HDFS也是類似上面這樣乾的,只不過它不是在NameNode起個定時的任務跑,而是用了一個新的角色:SecondNameNode。至於為什麼?可能HDFS覺得合併所耗費的資源太大了,不同的工作交由不同的伺服器來完成,也符合分散式的理念。
現在問題還是來了,此時的架構NameNode是單機的。SecondNameNode的作用只是給NameNode合併editlog
和fsimage
檔案,如果NameNode掛了,那client就請求不到了,而所有的請求都需要走NameNode,這導致整個HDFS叢集都不可用了。
於是我們需要保證NameNode是高可用的。一般現在我們會通過Zookeeper來實現。架構圖如下:
主NameNode和從NameNode需要保持元資料的資訊一致(因為如果主NameNode掛了,那從NameNode需要頂上,這時從NameNode需要有主NameNode的資訊)。
所以,引入了Shared Edits來實現主從NameNode之間的同步,Shared Edits也叫做JournalNode。實際上就是主NameNode如果有更新元資料的資訊,它的editlog
會寫到JournalNode,然後從NameNode會在JournalNode讀取到變化資訊,然後同步。從NameNode也實現了上面所說的SecondNameNode功能(合併editlog和fsimage)
稍微總結一下:
- NameNode需要處理client請求,它是儲存元資料的地方
- NameNode的元資料操作都在記憶體中,會把增刪改以
editlog
持續化到硬碟中(因為是順序io,所以不會太慢) - 由於
editlog
可能存在過大的問題,導致重新啟動NameNode過慢(因為要依賴editlog
來恢復資料),引出了fsimage
記憶體快照。需要跑一個定時任務來合併fsimage
和editlog
,引出了SecondNameNode
- 又因為NameNode是單機的,可能存在單機故障的問題。所以我們可以通過Zookeeper來維護主從NameNode,通過JournalNode(Share Edits)來實現主從NameNode元資料的一致性。最終實現NameNode的高可用。
2.3 學點DataNode
從上面我們就知道,我們的資料是存放在DataNode上的(還會備份)。
如果某個DataNode掉線了,那HDFS是怎麼知道的呢?
DataNode啟動的時候會去NameNode上註冊,他倆會維持心跳,如果超過時間閾值沒有收到DataNode的心跳,那HDFS就認為這個DataNode掛了。
還有一個問題就是:我們將Block存到DataNode上,那還是有可能這個DataNode的磁碟損壞了部分,而我們DataNode沒有下線,但我們也不知道損壞了。
一個Block除了存放資料的本身,還會存放一份元資料(包括資料塊的長度,塊資料的校驗和,以及時間戳)。DataNode還是會定期向NameNode上報所有當前所有Block的資訊,通過元資料就可校驗當前的Block是不是正常狀態。
最後
其實在學習HDFS的時候,你會發現很多的思想跟之前學過的都類似。就比如提到的Kafka、Elasticsearch這些常用的分散式元件。
如果對Kafka、Elasticsearch、Zookeeper、Redis等不瞭解的同學,可以在我的GitHub或公眾號裡邊找對應的文章哦~我覺得還算寫得通俗易懂的。
改天整合一下這些框架的持久化特點,再寫一篇(因為可以發現,他們的持久化機制都十分類似)
下一篇無意外的話,會寫寫MapReduce,感謝你看到這裡。
參考資料:
- HDFS漫畫
- 《從零開始學大資料 -李智慧》
如果大家想要實時關注我更新的文章以及分享的乾貨的話,可以關注我的公眾號「Java3y」。