我們在上一篇《分布式文件系統(tǒng)HDFS——基礎(chǔ)篇》中已經(jīng)已經(jīng)對HDFS的基本概念和操作進行了介紹贩绕,這一次我們將要更深入的了解一下HDFS畔乙。你已經(jīng)知道了HDFS是個分布式文件系統(tǒng)捆憎,那么你知道上傳到HDFS的文件存在哪里嗎郊闯?文件是怎么上傳上去的闺属?我們讀取文件的時候喇辽,HDFS又是怎么操作的呢?看完本篇或許你就有一個更清晰的認(rèn)識了,讓我們開始吧征唬!
特別提示:本文所進行的演示都是在hadoop-2.6.0-cdh5.15.1的版本進行的
一捌显、HDFS上數(shù)據(jù)的存儲
我們知道一般的磁盤文件系統(tǒng)都是通過硬盤驅(qū)動程序直接和物理硬件磁盤打交道,那HDFS也和它們一樣是直接操作物理磁盤嗎总寒?答案是否定的扶歪,HDFS和一般的磁盤文件系統(tǒng)不一樣,HDFS的存儲是基于本地磁盤文件系統(tǒng)的摄闸。
現(xiàn)在我們來做個小實驗善镰,讓你更清楚的認(rèn)識HDFS上的文件是怎么存的,實驗步驟如下(單機HDFS年枕、 Block Size:128M炫欺、 副本系數(shù):1):
1.我們上傳一個大小超過128M的文件到HDFS上
#要上傳的文件是JDK的壓縮包,大小186M
[root@hdh100 bin]# ll -h /app/jdk/jdk-8u231-linux-x64.tar.gz
-rw-r--r--. 1 root root 186M Jun 6 13:18 /app/jdk/jdk-8u231-linux-x64.tar.gz
#上傳到HDFS上的根目錄 ‘/’ 下
[root@hdh100 bin]# ./hadoop fs -put /app/jdk/jdk-8u231-linux-x64.tar.gz /
20/06/07 15:17:57 WARN util.NativeCodeLoader: Unable to load native-hadoop library for your platform... using builtin-java classes where applicable
上傳后效果如下:
從圖中看出熏兄,我們可以看出信息如下:
- a)185.16M 大小的JDK文件被分成了兩個Block品洛;
- b)兩個Block的大小加起來是整個文件的大小摩桶;
- c)兩個塊都存儲在主機hdh100上
2.找出兩個Block的存儲位置
如果你在安裝HDFS的時候在hdfs-site.xml中配置了hadoop.tmp.dir那么你的數(shù)據(jù)就存儲在你配置的目錄下毫别,如果你沒有配置這個選項數(shù)據(jù)默認(rèn)是存放在/tmp/hadoop-root下
#跳轉(zhuǎn)到數(shù)據(jù)的存放目錄
[root@hdh100 bin]# cd /tmp/hadoop-root/
#根據(jù)Block 0的BlockId查找數(shù)據(jù)塊的位置
[root@hdh100 hadoop-root]# find ./ -name *1073741844*
./dfs/data/current/BP-866925568-192.168.56.100-1591426054281/current/finalized/subdir0/subdir0/blk_1073741844_1020.meta
./dfs/data/current/BP-866925568-192.168.56.100-1591426054281/current/finalized/subdir0/subdir0/blk_1073741844
#根據(jù)Block 1的BlockId查找數(shù)據(jù)塊的位置
[root@hdh100 hadoop-root]# find ./ -name *1073741845*
./dfs/data/current/BP-866925568-192.168.56.100-1591426054281/current/finalized/subdir0/subdir0/blk_1073741845_1021.meta
./dfs/data/current/BP-866925568-192.168.56.100-1591426054281/current/finalized/subdir0/subdir0/blk_1073741845
#跳轉(zhuǎn)到塊所在的目錄
[root@hdh100 hadoop-root]# cd ./dfs/data/current/BP-866925568-192.168.56.100-1591426054281/current/finalized/subdir0/subdir0/
#查看兩個塊的大小,以'.meta'結(jié)尾的文件是元數(shù)據(jù)
[root@hdh100 subdir0]# ll | grep 107374184[45]
-rw-r--r--. 1 root root 134217728 Jun 7 15:18 blk_1073741844
-rw-r--r--. 1 root root 1048583 Jun 7 15:18 blk_1073741844_1020.meta
-rw-r--r--. 1 root root 59933611 Jun 7 15:18 blk_1073741845
-rw-r--r--. 1 root root 468239 Jun 7 15:18 blk_1073741845_1021.meta
經(jīng)過這個操作我們得到以下信息:
- a)HDFS的數(shù)據(jù)塊存儲在本地文件系統(tǒng)典格;
- b)兩個數(shù)據(jù)塊的大小和web看到的大小是一致的
那么HDFS是不是就是簡單的把我們上傳的文件給切分一下放置在這里了,它有做其他操作嗎台丛?我們繼續(xù)下一步
3.直接合并兩個文件耍缴,驗證我們想法
合并后無非是兩個結(jié)果:1.合并后的文件可以直接使用,則說明HDFS對上傳的文件是直接切分挽霉,對數(shù)據(jù)塊無特殊操作防嗡;2.合并后文件無法使用,則說明HDFS對拆分后文件進行了特殊處理
#將兩個塊合并
[root@hdh100 subdir0]# cat blk_1073741844 >> /test/jdk.tag.gz
[root@hdh100 subdir0]# cat blk_1073741845 >> /test/jdk.tag.gz
#跳轉(zhuǎn)到test目錄侠坎,test目錄是之前創(chuàng)建的
[root@hdh100 subdir0]# cd /test/
#嘗試解壓文件蚁趁,發(fā)現(xiàn)解壓成功
[root@hdh100 test]# tar -xzvf jdk.tag.gz
經(jīng)過上面操作,我們僅僅是將兩個塊進行了合并实胸,然后嘗試解壓發(fā)現(xiàn)竟然解壓成功了他嫡,這也證明了我們猜想HDFS 的文件拆分就是簡單拆分,拆分的塊合并后就直接是原文件庐完。
結(jié)論:從上面的實驗我們可以得出以下結(jié)論:a)HDFS的文件存儲是基于本地文件系統(tǒng)的钢属,也就是HDFS上的文件被切分后的塊是使用本地文件系統(tǒng)進行存儲;b)上傳文件到HDFS后门躯,HDFS對文件的拆分屬于基本拆分淆党,沒有對拆分后的塊進行特殊處理
二、HDFS寫數(shù)據(jù)流程
經(jīng)過上一步我們了解了HDFS上的文件存儲,現(xiàn)在我們開始看看HDFS創(chuàng)建一個文件的流程是什么染乌。
為了幫助大家理解山孔,錘子畫了一個圖,咱們跟著圖來看
結(jié)合圖荷憋,咱們來描述一下流程
1.客戶端調(diào)用DistributedFileSystem的create方法
2.DistributedFileSystem遠(yuǎn)程RPC調(diào)用NameNode台颠,請求在文件系統(tǒng)的命名空間中創(chuàng)建一個文件
3.NameNode進行相應(yīng)檢測,包括校驗文件是否已經(jīng)存在台谊,權(quán)限檢驗等蓉媳,校驗通過則創(chuàng)建一條新文件記錄,否則拋出未校驗通過的相應(yīng)異常锅铅,根據(jù)最后處理結(jié)果酪呻,對最請求端做出響應(yīng)
4.DistributedFileSystem跟NameNode交互接收到成功信息后,就會根據(jù)NameNode給出的信息創(chuàng)建一個FSDataOutputStream盐须。在FSDataOutputStream中持有一個final的DFSOutputStream引用玩荠,這個DFSOutputStream負(fù)責(zé)后續(xù)的NameNode和DataNode通信
5.輸出流準(zhǔn)備好后,客戶端就開始向輸出流寫數(shù)據(jù)
6.此時DataStreamer就會向NameNode發(fā)出請求創(chuàng)建一個新的Block
7.NameNode接收請求后經(jīng)過處理贼邓,創(chuàng)建一個新的BlockId阶冈,并且將該Block應(yīng)該存儲的位置返回給DataStream
DataStreamer是DFSOutputStream的一個內(nèi)部類,且DFSOutputStream中還持有一個DataStreamer實例的引用塑径。DataStreamer類負(fù)責(zé)向管道中的數(shù)據(jù)節(jié)點發(fā)送數(shù)據(jù)包女坑,它會向NameNode申請新的BlockId并檢索Block的位置,并開始將數(shù)據(jù)包流傳輸?shù)紻atanodes的管道统舀。
8.DateStreamer在接收到Block的位置后匆骗,會通知需要存儲該Block副本的相應(yīng)DataNode,讓這一組DataNode形成一個管道誉简。DataStreamer 就開始將數(shù)據(jù)包流傳輸?shù)紻atanodes的管道碉就,每一個DataNode節(jié)點接收到數(shù)據(jù)包流后存儲完成后就轉(zhuǎn)發(fā)給下一個節(jié)點,下一個節(jié)點存放之后就繼續(xù)往下傳遞闷串,直到傳遞到最后一個節(jié)點為止
9.放進DataNodes管道的這些數(shù)據(jù)包每個都有一個相關(guān)的序列號瓮钥,當(dāng)一個塊的所有數(shù)據(jù)包都被發(fā)送出去,并且每個包的ack(存儲成功應(yīng)答)都被接收到時烹吵,DataStreamer就關(guān)閉當(dāng)前塊了碉熄。(如果該文件有多個塊,后面就是按照6年叮,7具被,8,9步驟重復(fù))
10.文件數(shù)據(jù)全部寫入完畢后只损,客戶端發(fā)出關(guān)閉流請求一姿,待NameNode確認(rèn)所有數(shù)據(jù)寫入完成后七咧,就把成功信息返回給客戶端
三、HDFS讀數(shù)據(jù)流程
同樣讀數(shù)據(jù)流程我們也畫了一個圖
我們描述一下流程
1.客戶端調(diào)用DistributedFileSystem的open方法
2.DistributedFileSystem遠(yuǎn)程RPC調(diào)用NameNode叮叹,請求讀取指定文件
3.NameNode在接收請求后艾栋,查詢該文件的元數(shù)據(jù)信息忍法,包括文件Block起始塊位置阅懦,每個塊副本的DataNode地址,并將DataNode地址按照與當(dāng)前請求客戶端的距離遠(yuǎn)近進行排序掏击,并將信息返回給請求端
4.DistributedFileSystem跟NameNode交互成功接收到該文件的元數(shù)據(jù)信息后携冤,就會根據(jù)NameNode給出的信息創(chuàng)建一個FSDataInputStream悼粮。在FSDataInputStream中也持有一個DFSInputStream引用,DFSInputStream負(fù)責(zé)后續(xù)跟DataNode和NameNode通信
5.客戶端對FSDataInputStream發(fā)出read請求
6.DFSInputStream連接第一個塊的所有副本中與當(dāng)前客戶端最近的一個DataNode進行數(shù)據(jù)讀取曾棕,當(dāng)前塊讀取完成后繼續(xù)下一個塊扣猫,同樣尋找最佳的DataNode讀取,直到所有塊讀取完成
7.文件所有塊讀取完成后翘地,客戶端關(guān)閉流
四申尤、一致模型
文件系統(tǒng)的一致性模型是描述文件讀寫的數(shù)據(jù)可見性的。HDFS的一致模型中主要有以下特點
- 新建文件衙耕,它在文件系統(tǒng)的命名空間中立即可見
Path path = new Path("/dream-hammer.txt");
fileSystem.create(path);
fileSystem.exists(path) //結(jié)果為true
- 新建文件的寫入內(nèi)容不保證立即可見
Path path = new Path("/dream-hammer.txt");
OutputStram out = fileSystem.create(p);
out.write("content");
out.flush();
fileSystem.getFileStatus(path).getLen() //可能為0昧穿,即使數(shù)據(jù)已經(jīng)寫入
- 當(dāng)前寫入的Block對其他Reader不可見
即當(dāng)前客戶端正在寫入文件的某個Block,在寫的過程中橙喘,其他客戶端是看不到該Block的
hflush()方法:強行刷新緩存數(shù)據(jù)到DataNode时鸵,hflush刷新成功后,HDFS保證文件到目前為止對所有新的Reader而言都是可見的且寫入數(shù)據(jù)都已經(jīng)到達(dá)所有DataNode的寫入管道厅瞎。但是hflush不保證DataNode已經(jīng)將數(shù)據(jù)寫到磁盤寥枝,僅確保數(shù)據(jù)在DataNode的內(nèi)存中。
hsync()方法:也是一種強行刷新緩存數(shù)據(jù)到DataNode磁奖,在hsync()刷新成功后,客戶端所有的數(shù)據(jù)都發(fā)送到每個DataNode上某筐,并且每個DataNode上的每個副本都完成了POSIX中fsync的調(diào)用比搭,即操作系統(tǒng)已將數(shù)據(jù)存儲到磁盤上
調(diào)用hflush()和hsync()都會增加HDFS的負(fù)載,但是如果不使用強制緩存數(shù)據(jù)刷新南誊,就會有數(shù)據(jù)塊丟失的風(fēng)險身诺,對于大部分應(yīng)用來說因為客戶端或者系統(tǒng)系統(tǒng)故障丟失數(shù)據(jù)是不可接受的,所以實際生產(chǎn)中我們要根據(jù)實際場景來適當(dāng)調(diào)用抄囚,在數(shù)據(jù)安全和速度上進行平衡
文章歡迎轉(zhuǎn)載霉赡,轉(zhuǎn)載請注明出處,個人公眾號【愛做夢的錘子】幔托,全網(wǎng)同id穴亏,個站 http://te-amo.site蜂挪,歡迎關(guān)注,里面會分享更多有用知識嗓化,還有我的私密照片