HDFS 是一個能夠面向大規模數據使用的,可進行擴展的文件存儲與傳遞系統。是一種允許文件通過網絡在多台主機上分享的文件系統,可讓多機器上的多用戶分享文件和 存儲空間。讓實際上是通過網絡來訪問文件的動作,由程序與用戶看來,就像是訪問本地的磁盤一般。即使系統中有某些節點脫機,整體來說系統仍然可以持續運作 而不會有數據損失。
一、HDFS體系結構
1、Namenode
Namenode是整個文件系統的管理節點。它維護着整個文件系統的文件目錄樹,文件/目錄的元信息和每個文件對應的數據塊列表, 接收用戶的操作請求。
文件包括:
①fsimage:元數據鏡像文件。存儲某一時段NameNode內存元數據信息。
②edits:操作日志文件。
③fstime:保存最近一次checkpoint的時間
以上這些文件是保存在linux的文件系統中。通過hdfs-site.xml的dfs.namenode.name.dir屬性進行設置。
查看NameNode的fsimage與edits內容
這個兩個文件中的內容使用普通文本編輯器是無法直接查看的,幸運的是hadoop為此准備了專門的工具用於查看文件的內容,這些工具分別為oev和oiv,可以使用hdfs調用執行。
啟動服務器:bin/hdfs oiv -i 某個fsimage文件
bash$ bin/hdfs oiv -i fsimage
14/04/07 13:25:14 INFO offlineImageViewer.WebImageViewer: WebImageViewer started.
Listening on /127.0.0.1:5978. Press Ctrl+C to stop the viewer.
查看內容:bin/hdfs dfs -ls -R webhdfs://127.0.0.1:5978/
bash$ bin/hdfs dfs -ls webhdfs://127.0.0.1:5978/
Found 2 items
drwxrwx–* - root supergroup 0 2014-03-26 20:16 webhdfs://127.0.0.1:5978/tmp
drwxr-xr-x - root supergroup 0 2014-03-31 14:08 webhdfs://127.0.0.1:5978/user
導出fsimage的內容:bin/hdfs oiv -p XML -i
tmp/dfs/name/current/fsimage_0000000000000000055 -o fsimage.xml
bash$ bin/hdfs oiv -p XML -i fsimage -o fsimage.xml
0000055 -o fsimage.xml
查看edtis的內容:bin/hdfs oev -i
tmp/dfs/name/current/edits_0000000000000000057-0000000000000000186 -o edits.xml
bash$ bin/hdfs oev -i
tmp/dfs/name/current/edits_0000000000000000057-0000000000000000186 -o edits.xml
2、Datanode
提供真實文件數據的存儲服務。
文件塊( block): 最基本的存儲單位。
對於文件內容而言,一個文件的長度大小是size,那么從文件的0偏移開始,按照固定的大小,順序對文件進行划分並編號,划分好的每一個塊稱一個Block。 HDFS默認Block大小是128MB, 因此,一個256MB文件,共有256/128=2個Block.
與普通文件系統不同的是,在 HDFS中,如果一個文件小於一個數據塊的大小,並不占用整個數據塊存儲空間。
Replication:多復本。默認是三個。通過hdfs-site.xml的dfs.replication屬性進行設置。
二、數據存儲操作
1、數據存儲: block
默認數據塊大小為128MB,可配置。若文件大小不到128MB,則單獨存成一個block。
為何數據塊如此之大?
數據傳輸時間超過尋道時間(高吞吐率)
一個文件存儲方式?
按大小被切分成若干個block,存儲到不同節點上,默認情況下每個block有三個副本。
HDFS Block的設計理念:一個文件由哪些塊組成。一個塊存儲在哪些節點上。好處:易於分攤到各個節點。如下:
block1:node1,node2,node3
block2:node2,node3,node4
block3:node4,mode5,node6
block4:node5,node6.node7
2、數據存儲: staging
HDFS client上傳數據到HDFS時,首先,在本地緩存數據,當數據達到一個block大小時,請求NameNode分配一個block。 NameNode會把block所在的DataNode的地址告訴HDFS client。 HDFS client會直接和DataNode通信,把數據寫到DataNode節點一個block文件中。
3、數據存儲:讀文件操作
1.首先調用FileSystem對象的open方法,其實是一個DistributedFileSystem的實例。
2.DistributedFileSystem通過rpc獲得文件的第一批block的locations,同一個block按照重復數會返回多個locations,這些locations按照hadoop拓撲結構排序,距離客戶端近的排在前面。
3.前兩步會返回一個FSDataInputStream對象,該對象會被封裝DFSInputStream對象,DFSInputStream可 以方便的管理datanode和namenode數據流。客戶端調用read方法,DFSInputStream最會找出離客戶端最近的datanode 並連接。
4.數據從datanode源源不斷的流向客戶端。
5.如果第一塊的數據讀完了,就會關閉指向第一塊的datanode連接,接着讀取下一塊。這些操作對客戶端來說是透明的,客戶端的角度看來只是讀一個持續不斷的流。
6.如果第一批block都讀完了, DFSInputStream就會去namenode拿下一批block的locations,然后繼續讀,如果所有的塊都讀完,這時就會關閉掉所有的流。
如果在讀數據的時候, DFSInputStream和datanode的通訊發生異常,就會嘗試正在讀的block的排序第二近的datanode,並且會記錄哪個 datanode發生錯誤,剩余的blocks讀的時候就會直接跳過該datanode。 DFSInputStream也會檢查block數據校驗和,如果發現一個壞的block,就會先報告到namenode節點,然后 DFSInputStream在其他的datanode上讀該block的鏡像。
該設計就是客戶端直接連接datanode來檢索數據並且namenode來負責為每一個block提供最優的datanode, namenode僅僅處理block location的請求,這些信息都加載在namenode的內存中,hdfs通過datanode集群可以承受大量客戶端的並發訪問。
4、數據存儲:寫文件操作
1.客戶端通過調用DistributedFileSystem的create方法創建新文件。
2.DistributedFileSystem通過RPC調用namenode去創建一個沒有blocks關聯的新文件,創建前, namenode會做各種校驗,比如文件是否存在,客戶端有無權限去創建等。如果校驗通過, namenode就會記錄下新文件,否則就會拋出IO異常。
3.前兩步結束后,會返回FSDataOutputStream的對象,與讀文件的時候相似, FSDataOutputStream被封裝成DFSOutputStream。DFSOutputStream可以協調namenode和 datanode。客戶端開始寫數據到DFSOutputStream,DFSOutputStream會把數據切成一個個小的packet,然后排成隊 列data quene。
4.DataStreamer會去處理接受data quene,它先詢問namenode這個新的block最適合存儲的在哪幾個datanode里(比如重復數是3,那么就找到3個最適合的 datanode),把他們排成一個pipeline。DataStreamer把packet按隊列輸出到管道的第一個datanode中,第一個 datanode又把packet輸出到第二個datanode中,以此類推。
5.DFSOutputStream還有一個對列叫ack quene,也是由packet組成,等待datanode的收到響應,當pipeline中的所有datanode都表示已經收到的時候,這時akc quene才會把對應的packet包移除掉。
如果在寫的過程中某個datanode發生錯誤,會采取以下幾步:
1) pipeline被關閉掉;
2)為了防止防止丟包ack quene里的packet會同步到data quene里;
3)把產生錯誤的datanode上當前在寫但未完成的block刪掉;
4)block剩下的部分被寫到剩下的兩個正常的datanode中;
5)namenode找到另外的datanode去創建這個塊的復制。當然,這些操作對客戶端來說是無感知的。
6.客戶端完成寫數據后調用close方法關閉寫入流。
7.DataStreamer把剩余得包都刷到pipeline里,然后等待ack信息,收到最后一個ack后,通知datanode把文件標視為已完成。
注意:客戶端執行write操作后,寫完的block才是可見的,正在寫的block對客戶端是不可見的,只有 調用sync方法,客戶端才確保該文件的寫操作已經全部完成,當客戶端調用close方法時,會默認調用sync方法。是否需要手動調用取決你根據程序需 要在數據健壯性和吞吐率之間的權衡。