一,什么是分布式文件系統,分布式文件系統能干什么
在學習一個文件系統時,首先我先想到的是,學習它能為我們提供什么樣的服務,它的價值在哪里,為什么要去學它。以這樣的方式去理解它之后在日后的深入學習中才能夠對它有一個更深層次的理解。
1. 什么是分布式文件系統
所謂的分布式文件系統,我的個人理解是管理網絡中把數據存儲在不止一台機器上的系統,這樣的存儲方式被稱為分布式存儲。(管理網絡中跨多台計算機存儲的文件系統稱為分布式文件系統。)
2. 分布式文件系統能干什么
(1)分布式文件系統可以存儲數據量較大的文件。 (GB,TB,PB級別的海量數據)
(2)一次寫入,多次奪取。(一個文件經過創建、寫入和關閉之后就不需要改變。這一假設簡化了數據一致性問題,並且使高吞吐量的數據訪問成為可能。Map/Reduce應用或者網絡爬蟲應用都非常適合這個模型。目前還有計划在將來擴充這個模型,使之支持文件的附加寫操作。)
(3)搭建在普通的商業集群機器上就可以了,節省成本
二,HDFS分布式文件系統的生態環境
上圖基本可以說是目前整個Hadoop的生態圈結構圖,從圖中可以看出像Hive,Pig,HBase等數據處理框架都是應用在我們的HDFS分布式文件系統之上的。而且MapReduce計算框架也運行在HDFS上,對數據的上傳下載分析起到了至關重要的作用。
另外很多童鞋會把hdfs等價於hadoop的文件系統,其實hadoop是一個綜合文件系統抽象,而hdfs是hadoop旗艦級文件系統,hadoop除了hdfs還能集成其他文件系統。Hadoop的這個特點充分體現了hadoop的優良的可擴展性。
在hadoop里,hadoop定義了一個抽象的文件系統的概念,具體就是hadoop里面定義了一個java的抽象類:org.apache.hadoop.fs.FileSystm,這個抽象類用來定義hadoop中的一個文件系統接口,只要某個文件系統實現了這個接口,那么它就可以作為hadoop支持的文件系統。下面是目前實現了hadoop抽象文件類的文件系統,如下表所示:
文件系統 |
URI方案 |
Java實現 (org.apache.hadoop) |
定義 |
Local |
file |
fs.LocalFileSystem |
支持有客戶端校驗和本地文件系統。帶有校驗和的本地系統文件在fs.RawLocalFileSystem中實現。 |
HDFS |
hdfs |
hdfs.DistributionFileSystem |
Hadoop的分布式文件系統。 |
HFTP |
hftp |
hdfs.HftpFileSystem |
支持通過HTTP方式以只讀的方式訪問HDFS,distcp經常用在不同的HDFS集群間復制數據。 |
HSFTP |
hsftp |
hdfs.HsftpFileSystem |
支持通過HTTPS方式以只讀的方式訪問HDFS。 |
HAR |
har |
fs.HarFileSystem |
構建在Hadoop文件系統之上,對文件進行歸檔。Hadoop歸檔文件主要用來減少NameNode的內存使用。 |
KFS |
kfs |
fs.kfs.KosmosFileSystem |
Cloudstore(其前身是Kosmos文件系統)文件系統是類似於HDFS和Google的GFS文件系統,使用C++編寫。 |
FTP |
ftp |
fs.ftp.FtpFileSystem |
由FTP服務器支持的文件系統。 |
S3(本地) |
s3n |
fs.s3native.NativeS3FileSystem |
基於Amazon S3的文件系統。 |
S3(基於塊) |
s3 |
fs.s3.NativeS3FileSystem |
基於Amazon S3的文件系統,以塊格式存儲解決了S3的5GB文件大小的限制。 |
最后我要強調一點:在hadoop里有一個文件系統概念,例如上面的FileSystem抽象類,它是位於hadoop的Common項目里,主要是定義一組分布式文件系統和通用的I/O組件和接口,hadoop的文件系統准確的應該稱作hadoop I/O。而HDFS是實現該文件接口的hadoop自帶的分布式文件項目,hdfs是對hadoop I/O接口的實現。下面我給大家展示一張表,這樣大家對hadoop的FileSystem里的相關API操作就比較清晰了,表如下所示:
Hadoop的FileSystem |
Java操作 |
Linux操作 |
描述 |
URL.openSteam FileSystem.open FileSystem.create FileSystem.append |
URL.openStream |
open |
打開一個文件 |
FSDataInputStream.read |
InputSteam.read |
read |
讀取文件中的數據 |
FSDataOutputStream.write |
OutputSteam.write |
write |
向文件寫入數據 |
FSDataInputStream.close FSDataOutputStream.close |
InputSteam.close OutputSteam.close |
close |
關閉一個文件 |
FSDataInputStream.seek |
RandomAccessFile.seek |
lseek |
改變文件讀寫位置 |
FileSystem.getFileStatus FileSystem.get* |
File.get* |
stat |
獲取文件/目錄的屬性 |
FileSystem.set* |
File.set* |
Chmod等 |
改變文件的屬性 |
FileSystem.createNewFile |
File.createNewFile |
create |
創建一個文件 |
FileSystem.delete |
File.delete |
remove |
從文件系統中刪除一個文件 |
FileSystem.rename |
File.renameTo |
rename |
更改文件/目錄名 |
FileSystem.mkdirs |
File.mkdir |
mkdir |
在給定目錄下創建一個子目錄 |
FileSystem.delete |
File.delete |
rmdir |
從一個目錄中刪除一個空的子目錄 |
FileSystem.listStatus |
File.list |
readdir |
讀取一個目錄下的項目 |
FileSystem.getWorkingDirectory |
|
getcwd/getwd |
返回當前工作目錄 |
FileSystem.setWorkingDirectory |
|
chdir |
更改當前工作目錄
|
有了這張表,大家對FileSystem的理解應該會清晰多了吧。
大家從對照表里會發現,hadoop的FileSystem里有兩個類:FSDataInputStream和FSDataOutputStream類,它們相當於java I/O里的InputStream和Outputsteam,而事實上這兩個類是繼承java.io.DataInputStream和java.io.DataOutputStream。
至於關於hadoop I/O本文今天不做介紹,以后也許會專門寫篇文章講講我自己的理解,不過為了給大家一個清晰的印象,我在博客園里找到了兩篇文章,有興趣的童鞋可以好好看看看,連接如下:
http://www.cnblogs.com/xuqiang/archive/2011/06/03/2042526.html
http://www.cnblogs.com/xia520pi/archive/2012/05/28/2520813.html
三,HDFS分布式文件系統對數據是如何進行讀寫的
1. HDFS 寫入過程
NameNode負責管理存儲在HDFS上所有文件的元數據,它會確認客戶端的請求,並記錄下文件的名字和存儲這個文件的DataNode集合。它把該信息存儲在內存中的文件分配表里。
例如,客戶端發送一個請求給NameNode,說它要將“zhou.log”文件寫入到HDFS。那么,其執行流程如圖1所示。具體為:
第一步:客戶端發消息給NameNode,說要將“zhou.log”文件寫入。(如圖1中的①)
第二步:NameNode發消息給客戶端,叫客戶端寫到DataNode A、B和D,並直接聯系DataNode B。(如圖1中的②)
第三步:客戶端發消息給DataNode B,叫它保存一份“zhou.log”文件,並且發送一份副本給DataNode A和DataNode D。(如圖1中的③)
第四步:DataNode B發消息給DataNode A,叫它保存一份“zhou.log”文件,並且發送一份副本給DataNode D。(如圖1中的④)
第五步:DataNode A發消息給DataNode D,叫它保存一份“zhou.log”文件。(如圖1中的⑤)
第六步:DataNode D發確認消息給DataNode A。(如圖1中的⑤)
第七步:DataNode A發確認消息給DataNode B。(如圖1中的④)
第八步:DataNode B發確認消息給客戶端,表示寫入完成。(如圖1中的⑥)
圖1 HDFS寫過程示意圖
在分布式文件系統的設計中,挑戰之一是如何確保數據的一致性。對於HDFS來說,直到所有要保存數據的DataNodes確認它們都有文件的副本時,數據才被認為寫入完成。因此,數據一致性是在寫的階段完成的。一個客戶端無論選擇從哪個DataNode讀取,都將得到相同的數據。
2. HDFS 讀的過程
為了理解讀的過程,可以認為一個文件是由存儲在DataNode上的數據塊(block)組成的。客戶端查看之前寫入的內容的執行流程如圖2所示,具體步驟為:
第一步:客戶端詢問NameNode它應該從哪里讀取文件。(如圖2中的①)
第二步:NameNode發送數據塊的信息給客戶端。(數據塊信息包含了保存着文件副本的DataNode的IP地址,以及DataNode在本地硬盤查找數據塊所需要的數據塊ID。) (如圖2中的②)
第三步:客戶端檢查數據塊信息,聯系相關的DataNode,請求數據塊(這里涉及到一個就近原則問題,NameNode把數據塊信息反饋給客戶端后,客戶端會選擇離自己最近的DataNode上去讀取數據)。(如圖2中的③)
第四步:DataNode返回文件內容給客戶端,然后關閉連接,完成讀操作。(如圖2中的④)
圖2 HDFS讀過程示意圖
客戶端並行從不同的DataNode中獲取一個文件的數據塊,然后聯結這些數據塊,拼成完整的文件。
四,通過副本快速恢復硬件故障
當一切運行正常時,DataNode會周期性發送心跳信息給NameNode(默認是每3秒鍾一次)。如果NameNode在預定的時間內沒有收到心跳信息(默認是10分鍾),它會認為DataNode出問題了,把它從集群中移除,並且啟動一個進程去恢復數據。DataNode可能因為多種原因脫離集群,如硬件故障、主板故障、電源老化和網絡故障等。
對於HDFS來說,丟失一個DataNode意味着丟失了存儲在它的硬盤上的數據塊的副本。假如在任意時間總有超過一個副本存在(默認3個),故障將不會導致數據丟失。當一個硬盤故障時,HDFS會檢測到存儲在該硬盤的數據塊的副本數量低於要求,然后主動創建需要的副本,以達到滿副本數狀態。
五,跨多個DataNode切分文件
在HDFS里,文件被切分成數據塊,通常每個數據塊64MB~128MB,然后每個數據塊被寫入文件系統。同一個文件的不同數據塊不一定保存在相同的DataNode上。這樣做的好處是,當對這些文件執行運算時,能夠通過並行方式讀取和處理文件的不同部分。
當客戶端准備寫文件到HDFS並詢問NameNode應該把文件寫到哪里時,NameNode會告訴客戶端,那些可以寫入數據塊的DataNode。寫完一批數據塊后,客戶端會回到NameNode獲取新的DataNode列表,把下一批數據塊寫到新列表中的DataNode上。