Hadoop 是什么
Hadoop 是一個開源的大數據框架同時也是一個分布式計算的解決方案。Hadoop = HDFS (分布式文件系統)+MapReduce(分布式計算)
HDFS
HDFS 概念
-
數據塊
-
NameNode
-
DataNode
數據塊:
數據塊是一個抽象的塊,而不是整個文件。默認大小是64Mb,一般設置為128Mb,備份x3 數據塊的大小可以隨着磁盤傳輸速率的提升而得到增加。
HDFS的塊比磁盤的大,主要是減少尋址時間在整個文件傳輸時間中的占比。比如為了讓磁盤尋址時間只占到整個文件傳輸時間的1%,而尋址時間為10ms,磁盤的IO傳輸速率為100Mb/s,那么一個塊的大小要大於100Mb才能達到這個要求。隨着以后磁盤的傳輸速率越來越高,塊的大小也會越來越大的。 【但是塊的大小也不會很大,因為MapReduce中的map任務一次只處理一個塊的數據,如果map任務過少(少於集群的節點數量),作業的運行效率也會比較慢】
對數據塊進行抽象的好處:
-
一個文件的大小可以大於集群網絡中任一個磁盤的大小。因為可以對文件進行分塊存儲,所以在一種極端情況下,一個集群只存放了一個文件,該文件占滿了集群中的所有磁盤。
-
使用抽象塊而不是整個文件作為存儲單元,可以簡化存儲子系統的設計。首先塊的大小是固定的,所以一個磁盤能夠存儲多少個塊很容易就能夠計算出來。另外也消除了對於元數據的顧慮,塊只是要存儲的大塊數據,而文件的元數據,例如權限信息等等,並不需要與塊進行一同存儲,可以進行單獨管理。
-
塊適合於提供備份和冗余容錯的作用,通過將塊進行復制副本,通常是3個,當因損壞或者機器故障而丟失的塊,我們便可以從其他候選機器將副本塊復制到另一台能夠正常工作的機器上,保證副本的數量保持不變。
HDFS集群有兩類節點,以管理節點-工作節點的模式運行着,分別是NameNode和DataNode。
NameNode
NameNode :
-
管理文件系統的命名空間,存放着元數據 (對應文件:命名空間鏡像文件)
-
維護着文件系統樹以及整棵樹內所有的文件以及目錄 (對應文件:編輯日志)
-
記錄着每個文件各個塊所在的數據節點信息,但並不會一直保存塊的位置信息,因為在重啟時會根據數據節點信息重建。
聯邦HDFS
NameNode節點在內存中保存着文件系統中每個文件和每個數據塊的引用關系,所以NameNode的內存大小會成為集群擴展的一個瓶頸。
在Hadoop 2.x版本中引入了聯邦HDFS,允許在集群中添加多個NameNode節點,以實現擴展。
在聯邦環境下,每個NameNode都維護着一個命名空間卷( 比如NameNode_1負責 /usr NameNode_2 負責 /share ),由命名空間的元數據和數據塊池組成。
數據塊池里面存放着該命名空間下所有的數據塊。
命名空間卷之間不進行通信,甚至其中一個掛掉也不會影響另一個。但每個DataNode需要注冊到每一個NameNode上,也需要存儲着來自各個數據塊池的數據塊。
DataNode
-
存儲以及檢索數據塊
-
向NameNode更新所存儲塊的列表
NameNode容錯
若NameNode失效,則我們無法訪問到文件系統上的所有文件。因為我們不知道怎么去根據DataNode的塊去重建文件。因此需要對NameNode進行容錯處理。
兩種容錯機制:
-
對於組成文件系統元數據持久狀態的文件 我們可以使NameNode在多個文件系統上對其進行保存,比如最常用的就是在將持久狀態寫入本地磁盤的同時也將其寫入到遠程掛載的NFS網絡文件系統中。
-
運行一個輔助NameNode節點 (Secondary NameNode)該節點卻不能被用作NameNode,它的主要作用是定期合並編輯日志文件和命名空間鏡像文件,防止其過大。它在合並后會生成命名空間鏡像文件的副本,當NameNode失效時會啟用。但是輔助NameNode節點保存的信息總是會滯后於NameNode節點,所以如果想要實現容錯機制,可以在主NameNode節點失效后,將保存在NFS的文件系統元數據復制到Secondary NamoNode上來,將其作為新的主NameNode運行。
HA高可用
HDFS 優點
-
適合大文件存儲,支持TB、PB級文件的存儲
-
可以構建在廉價的機器上,並能夠提供容錯機制和恢復機制
-
支持流式數據訪問,一次寫入,多次讀取更高效
HDFS 缺點
-
不適合大量小文件存儲
-
不適合並發寫入,也不支持文件隨機修改
-
不支持隨機讀等低延遲的訪問方式
HDFS 讀寫流程
HDFS 讀流程
HDFS讀流程圖 (圖片來源-慕課網)
假設現在有三個DataNode節點,分別存放着數據Data數據塊1、數據塊2、數據塊1、2,則如果此時客戶端想要請求Data數據,流程如下:
-
Client向NameNode發出請求,請求Data文件。
-
NameNode通過其所維護的相關的數據塊的信息,會把該Data文件的所有block的所有的DataNode信息返回給Client。
-
然后Client隨即從距離(按照帶寬進行計算出來的距離) 最近且保存着文件第一個塊的DataNode節點上讀取數據
-
在讀取完第一個塊的數據以后,便會尋找下一個塊的最佳DataNode,並從其上讀取數據,直到將所有的Data文件塊數據讀取完畢
-
如果在讀取的過程中,遇到問題(比如讀取的DataNode節點掛掉了),則客戶端會再次去尋找存有該塊信息副本的DataNode節點,並從其上讀取出塊數據。
如下圖所示:
然而上述的流程有些地方不夠完善。客戶端是怎么去讀取塊的數據的?讀過程對於客戶端而言是否是透明的?依據距離去尋找最優的DataNode節點,這個距離是如何計算的?
詳細的讀流程圖如下所示:
客戶端如何讀取DataNode上所存的塊數據?
步驟一:客戶端通過FileSyste對象的open()方法去打開希望讀取的文件;
步驟二:此時,DistributedFileSystem會通過RPC去調用NameNode,然后NameNode會返回存有該文件所有block的所有的DataNode信息 (對於每一個塊,NameNode會返回存有該塊副本的DataNode信息,並按照距離對DataNode進行排序);
步驟三:然后,DistributedFileSystem類會返回一個FSDataInputStream對象給客戶端去讀取數據
-
該FSDataInputStream類會封裝DFSInputStream對象,該對象管理着datanode和namenode的I/O
步驟四:客戶端對這個數據流調用read()方法去讀取塊數據:
-
DFSInputStream里存放着文件前幾個塊的副本的所有DataNode地址,於是會去連接距離最近的DataNode
步驟五:Client通過對數據流反復調用read()方法,在DFSInputStream讀取完第一個塊的數據以后,會關閉與該DataNode的連接,轉而去連接下一個距離最近且存着第二個塊數據的DataNode節點,繼續讀取數據。整個過程對於Client而言是完全透明的,在客戶端而言,它一直在讀取一個連續的數據流
步驟六:Client在讀取了文件前幾個塊的數據以后,根據需要,Client可能會詢問NameNode節點檢索下一批DataNode數據塊的位置,然后繼續通過DFSInputStream去讀取數據。客戶端一旦完成了數據讀取,便會對FSDataInputStream調用close()方法。
讀取流程的細節:
在讀取過程中,如果某一個DataNode發生故障,DFSInputStream會嘗試連接另外一個最臨近的DataNode,並記下該故障DataNode,保證后面不會再去該節點讀取數據。DFSInputStream會通過校驗和去檢查讀取的文件的正確性。
這種讀取流程,讓NameNode的工作量大大減少,只需要響應客戶端的塊位置請求即可,無需響應數據請求,所以可以支持高擴展。
距離是按照帶寬來進行計算的,一般來說可以依據場景,對帶寬進行遞減: 1.同一節點的不同進程 2.同一機架的不同節點 3.同一數據中心的不同機架上的節點 4.不同數據中心的節點
HDFS寫流程
HDFS寫流程圖 (圖片來源-慕課網)
-
首先,客戶端會向NameNode請求寫入文件
-
NameNode通過查詢自己維護的節點信息,向客戶端返回還有空間可以存放數據的DataNode節點信息
-
客戶端將data文件進行分塊,然后將塊內容以及NameNode發送給客戶端的信息一起發送給DataNode-1
-
DataNode-1收到以后,會在管線中進行塊數據的備份復制,使得達到塊數據的最小復本數
-
當DataNode-1-2-3都存儲完數據塊以后,將會反饋給NameNode存儲完成數據塊-1信息,NameNode更新一下元數據信息,接着NameNode再將該信息返回給客戶端
-
客戶端再次開始存儲數據塊-2
然而上述的流程還不夠清晰,有很多細節值得去深入了解一下。比如: 客戶端是如何將塊文件寫入到DataNode中的,是以塊為單位直接傳輸嗎? DataNode如何實現將數據備份到其他DataNode節點上的呢? 如果在寫入塊數據的過程中,發生了錯誤,HDFS會怎么處理呢?
客戶端如何將文件寫入到DataNode?
create:
-
客戶端通過DistributedFileSystem對象調用create()方法來創建一個新文件
-
DistributedFileSystem的create()方法會返回一個FSDataOutputStream對象
-
FSDataOutputStream封裝了一個DFSOutputStream對象,在其構造函數中會使用RPC遠程調用NameNode的create()方法來創建一個文件
-
NameNode會先對文件創建操作進行檢查(比如該客戶端有沒有權限創建文件,該文件名是否已經存在?),若檢查沒問題,NameNode則為創建新文件做一條記錄,否則創建失敗,並向客戶端返回IOException異常
write packet 和 ack packet:
客戶端寫入數據塊時,DFSOutputStream將其分成一個一個的包(packet),將packet放到pipeline(管線)里進行寫入。寫入時,會使用兩個隊列,一個是“數據隊列”用於存放要寫入packet;一個則是“確認隊列“,用於接收DataNode發來的確認Ack。
寫入過程:(DataStreamer負責處理)
-
一般會使用FSDataOutputStream的write方法
-
FSDataOutputStream的write方法會調用DFSOutputStream的write方法,而DFSOutputStream繼承自FSOutputSummer,所以實際上是調用FSOutputSummer的write方法
-
首先將package 1寫入DataNode 1然后由DataNode 1負責將package 1寫入DataNode 2,同時客戶端可以將pacage 2寫入DataNode 1
-
然后DataNode 2負責將package 1寫入DataNode 3, 同時客戶端可以講package 3寫入DataNode 1,DataNode 1將package 2寫入DataNode 2
-
就這樣將一個個package排着隊的傳遞下去,直到所有的數據全部寫入並復制完畢
確認過程:
-
DataStreamer線程負責把准備好的數據packet,順序寫入到DataNode,未確認寫入成功的packet則移動到ackQueue,等待確認。
-
DataStreamer線程傳輸數據到DataNode時,要向namenode申請數據塊,在NameNode分配了DataNode和block以后,createBlockOutputStream開始寫入數據。
-
只有對於一個數據包(packet)收到管道內的所有DataNode的ack之后,才能將該數據包從確認隊列中刪除。
當寫入時有DataNode發生故障,導致數據無法正常寫入,該怎么處理??
DataStreamer會啟動ResponseProcessor線程,它負責接收datanode的ack
-
首先將管線關閉
-
將確認隊列的數據包添加回數據隊列的前端
-
將發生故障的DataNode從管線中移除
-
在另一個正常的DataNode節點對當前的數據塊做一個標記,並將標識發給NameNode,使損壞的DataNode恢復正常后能夠刪除已存儲的部分數據塊
-
通過RPC調用DataNode的recoverBlock方法來恢復數據塊
-
以剩下的DataNode節點建立新的管線,繼續寫入數據(NameNode注意到塊復本數量不足時,會重新添加一個DataNode進行副本數據保存)
HDFS如何選擇副本的存儲位置?
副本存放位置的選定需要同時對可靠性、寫入帶寬和讀取帶寬同時均衡考量進行選取 默認布局:
-
第一個副本一般放在客戶端,如果客戶端在數據中心之外,則會隨機在數據中心選擇一個節點
-
第二個副本,選擇和第一個在不同的機架的一個節點
-
第三個副本則選擇和第二個副本在一個機架上,但是是不同的節點
-
后面的副本會隨機選擇,不過系統會盡量避免一個機架上會存放過多的副本