概念
文件系統
磁盤進行讀寫的最小單位:數據塊,文件系統構建於磁盤之上,文件系統的塊大小是磁盤塊的整數倍。
文件系統塊一般為幾千字節,磁盤塊一般512字節。
hdfs的block、pocket、chunk
-
block
hdfs的塊,常說的block,是這三個里最大的單位。默認128MB(配置參數:dfs.block.size)。
128MB的原因:塊太小會增加尋址時間;塊太大會減少Map的任務(通常一個Map只處理一個塊的數據)。
注:文件的大小小於一個block並不會占據整個block的空間,如一個1M的文件存儲在128MB的block中時,並不是占用128MB的的磁盤空間,而是1MB。
-
pocket
這三個里面中等大小的單位,DFSClient流向DataNode的粒度,以dfs.write.packet.size參數為參考值,默認是64K;注:這個參數為參考值,是指真正在進行數據傳輸時,會以它為基准進行調整,調整的原因是一個packet有特定的結構,調整的目標是這個packet的大小剛好包含結構中的所有成員,同時也保證寫到DataNode后當前block的大小不超過設定值;
-
chunk
是最小的一個單位,它是DFSClient到DataNode數據傳輸中進行數據校驗的粒度,由io.bytes.per.checksum參數決定,默認是512B;事實上一個chunk還包含4B的校驗值,因而chunk寫入packet時是516B;數據與檢驗值的比值為128:1,所以對於一個128M的block會有一個1M的校驗文件與之對應;
分布式文件系統使用塊的好處
- 一個文件的大小可以大於網絡中任意磁盤的容量。
- 使用抽象快而不是整個文件作為存儲,大大簡化了存儲子系統的設計。
文件讀
- 客戶端調用FileSyste對象的open()方法打開要讀取的文件。
- DistractedFileSystem通過遠程過程調用(RPC)來調用namenode,以獲取文件的其實位置。對於每一個塊,namenode返回該副本的datanode的地址。這些datanode根據它們與客戶端的距離來排序(根據集群的網絡拓撲)。如果客戶端本身就是一個datanode,那么會從保存相應數據塊副本的本地datanode讀取數據。
- DistrubutedFileSystem返回一個FSDataInputStream對象(支持文件定位的數據流)給客戶端便讀取數據。FSDataInputStream轉而封裝DFSInputStream對象,它管理datanode和namenode的I/O。接着客戶端對這個數據流調用read()。
- 存儲着文件的塊的datanode地址的DFSInputStream會連接距離最近的文件中第一個塊所在的datanode。反復調用read()將數據從datanode傳輸到客戶端。
- 讀到塊的末尾時,DFSInputStream關閉與前一個datanode的連接,然后尋找下一個塊的最佳datanode。
- 客戶端的讀寫順序時按打開的datanode的順序讀的,一旦讀取完成,就對FSDataIputStream調用close()方法。
在讀取數據的時候,datanode一旦發生故障,DFSInputStream會嘗試從這個塊鄰近的datanode讀取數據,同時也會記住哪個故障的datanode,並把它通知到namenode。
文件寫
- 客戶端通過調用DistributedFileSystem的create()方法新建文件。
- DistributedFileSystem對namenode創建RPC調用,在文件系統的命名空間新建一個文件,此時該文件還沒有相應的數據塊。
- namenode執行各種檢查以確保這個文件不存在以及客戶端新建文件的權限。如果各種檢查都通過,就創建;否則拋出IO異常。DistributedFileSystem向客戶端返回一個FSDataOutputStream對象,由此客戶端開始寫入數據,FSDataOutputStream會封裝一個DFSoutPutstream對象,負責namenode和datanode之間的通信。
- DFSOutPutstream將數據分成一個個的數據包(packet),並寫入內部隊列,即數據隊列(data queue),DataStreamer處理數據隊列,它將選擇一組datanode,並據此要求namenode重新分配新的數據塊。這一組datanode構成管線,假設副本數是3,所以管線有3個節點。DataStreamer將數據包流式傳輸到第一個datanode,該datanode存儲數據包並發送給第二個datanode。。。依次類推,直到最后一個。【面試題】
- DFSOutPutstream維護了一個數據包隊列等待datanode的收到確認回執,成為確認隊列(ack queue),每一個datanode收到數據包后都會返回一個確認回執,然后放到這個ack queue,等所有的datanode確認信息后,該數據包才會從隊列ack queue刪除。
- 完成數據寫入后,對數據流調用close。
在寫入過程中datanode發生故障,將執行以下操作
1)關閉管線,把隊列的數據報都添加到隊列的最前端,以確保故障節點下游的datanode不會漏掉任何一個數據包。
2)為存儲在另一個正常的datanode的當前數據塊指定一個新的標識,並把標識發送給namenode,以便在datanode恢復正常后可以刪除存儲的部分數據塊。
3)從管線中刪除故障datanode,基於正常的datanode構建一條新管線。余下的數據塊寫入管線中正常的datanode。namenode注意到塊副本數量不足,會在另一個節點上創建一個新的副本。后續的數據塊正常接受處理。
如果多個datanode發生故障(非常少見)
只要寫入了dfs.namenode.replication.min的副本數(默認1),寫操作就會成功,並且這個塊可以在集群中異步復制,直到達到其目的的副本數(dfs.replication的默認值3)
參考
hadoop權威指南