學習完Hadoop權威指南有一段時間了,如今再回顧和總結一下HDFS的知識點。html
一、HDFS的設計java
HDFS是什麼:HDFS即Hadoop分佈式文件系統(Hadoop Distributed Filesystem),以流式數據訪問模式來存儲超大文件,運行於商用硬件集羣上,是管理網絡中跨多臺計算機存儲的文件系統。node
HDFS不適合用在:要求低時間延遲數據訪問的應用,存儲大量的小文件,多用戶寫入,任意修改文件。數據庫
二、HDFS的概念apache
HDFS數據塊:HDFS上的文件被劃分爲塊大小的多個分塊,做爲獨立的存儲單元,稱爲數據塊,默認大小是64MB。數組
使用數據塊的好處是:網絡
查看塊信息架構
HDFS的三個節點:Namenode,Datanode,Secondary Namenodetcp
Namenode:HDFS的守護進程,用來管理文件系統的命名空間,負責記錄文件是如何分割成數據塊,以及這些數據塊分別被存儲到那些數據節點上,它的主要功能是對內存及IO進行集中管理。分佈式
Datanode:文件系統的工做節點,根據須要存儲和檢索數據塊,而且按期向namenode發送他們所存儲的塊的列表。
Secondary Namenode:輔助後臺程序,與NameNode進行通訊,以便按期保存HDFS元數據的快照。
HDFS Federation(聯邦HDFS):
經過添加namenode實現擴展,其中每一個namenode管理文件系統命名空間中的一部分。每一個namenode維護一個命名空間卷,包括命名空間的源數據和該命名空間下的文件的全部數據塊的數據塊池。
HDFS的高可用性(High-Availability)
Hadoop的2.x發行版本在HDFS中增長了對高可用性(HA)的支持。在這一實現中,配置了一對活動-備用(active-standby)namenode。當活動namenode失效,備用namenode就會接管它的任務並開始服務於來自客戶端的請求,不會有明顯的中斷。
架構的實現包括:
故障轉移控制器:管理着將活動namenode轉移給備用namenode的轉換過程,基於ZooKeeper並由此確保有且僅有一個活動namenode。每個namenode運行着一個輕量級的故障轉移控制器,其工做就是監視宿主namenode是否失效並在namenode失效時進行故障切換。
三、命令行接口
兩個屬性項: fs.default.name 用來設置Hadoop的默認文件系統,設置hdfs URL則是配置HDFS爲Hadoop的默認文件系統。dfs.replication 設置文件系統塊的副本個數
文件系統的基本操做:hadoop fs -help能夠獲取全部的命令及其解釋
經常使用的有:
HDFS的文件訪問權限:只讀權限(r),寫入權限(w),可執行權限(x)
四、Hadoop文件系統
Hadoop有一個抽象的文件系統概念,HDFS只是其中的一個實現。Java抽象接口org.apache.hadoop.fs.FileSystem定義了Hadoop中的一個文件系統接口。該抽象類實現HDFS的具體實現是 hdfs.DistributedFileSystem
五、Java接口
最簡單的從Hadoop URL讀取數據 (這裏在Eclipse上鍊接HDFS編譯運行)
package filesystem; import java.io.IOException; import java.io.InputStream; import java.net.MalformedURLException; import java.net.URL; import org.apache.hadoop.fs.FsUrlStreamHandlerFactory; import org.apache.hadoop.io.IOUtils; public class URLCat { static { URL.setURLStreamHandlerFactory(new FsUrlStreamHandlerFactory()); } public static void main(String[] args) throws MalformedURLException, IOException { InputStream in = null; String input = "hdfs://192.168.92.138:9000/user/test.txt"; try { in = new URL(input).openStream(); IOUtils.copyBytes(in, System.out, 4096,false); }finally { IOUtils.closeStream(in); } } }
這裏調用Hadoop的IOUtils類,在輸入流和輸出流之間複製數據(in和System.out)最後兩個參數用於第一個設置複製的緩衝區大小,第二個設置結束後是否關閉數據流。
還能夠經過FileSystem API讀取數據
代碼以下:
package filesystem; import java.io.IOException; import java.io.InputStream; import java.net.URI; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.fs.FileSystem; import org.apache.hadoop.fs.Path; import org.apache.hadoop.io.IOUtils; public class FileSystemCat { public static void main(String[] args) throws IOException { String uri = "hdfs://192.168.92.136:9000/user/test.txt"; Configuration conf = new Configuration(); FileSystem fs = FileSystem.get(URI.create(uri),conf); InputStream in = null; try { in = fs.open(new Path(uri)); IOUtils.copyBytes(in, System.out, 1024,false); }finally { IOUtils.closeStream(in); } } }
這裏調用open()函數來獲取文件的輸入流,FileSystem的get()方法獲取FileSystem實例。
使用FileSystem API寫入數據
代碼以下:
package filesystem; import java.io.BufferedInputStream; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.InputStream; import java.io.OutputStream; import java.net.URI; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.fs.FileSystem; import org.apache.hadoop.fs.Path; import org.apache.hadoop.io.IOUtils; import org.apache.hadoop.util.Progressable; public class FileCopyWithProgress { public static void main(String[] args) throws Exception { String localSrc = "E:\\share\\input\\2007_12_1.txt"; String dst = "hdfs://192.168.92.136:9000/user/logs/2008_10_2.txt"; InputStream in = new BufferedInputStream(new FileInputStream(localSrc)); Configuration conf = new Configuration(); FileSystem fs = FileSystem.get(URI.create(dst),conf); OutputStream out = fs.create(new Path(dst),new Progressable() { public void progress() { System.out.print("*"); } }); IOUtils.copyBytes(in, out, 1024,true); } }
FileSystem的create()方法用於新建文件,返回FSDataOutputStream對象。 Progressable()用於傳遞迴掉窗口,能夠用來把數據寫入datanode的進度通知給應用。
使用FileSystem API刪除數據
代碼以下:
package filesystem; import java.net.URI; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.fs.FileSystem; import org.apache.hadoop.fs.Path; public class FileDelete { public static void main(String[] args) throws Exception{ String uri = "hdfs://192.168.92.136:9000/user/1400.txt"; Configuration conf = new Configuration(); FileSystem fs = FileSystem.get(URI.create(uri),conf); fs.delete(new Path(uri)); } }
使用delete()方法來永久性刪除文件或目錄。
FileSystem的其它一些方法:
六、數據流
HDFS讀取文件過程:
過程描述:
(1)客戶端調用FileSyste對象的open()方法在分佈式文件系統中打開要讀取的文件。
(2)分佈式文件系統經過使用RPC(遠程過程調用)來調用namenode,肯定文件起始塊的位置。
(3)分佈式文件系統的DistributedFileSystem類返回一個支持文件定位的輸入流FSDataInputStream對象,FSDataInputStream對象接着封裝DFSInputStream對象(存儲着文件起始幾個塊的datanode地址),客戶端對這個輸入流調用read()方法。
(4)DFSInputStream鏈接距離最近的datanode,經過反覆調用read方法,將數據從datanode傳輸到客戶端。
(5) 到達塊的末端時,DFSInputStream關閉與該datanode的鏈接,尋找下一個塊的最佳datanode。
(6)客戶端完成讀取,對FSDataInputStream調用close()方法關閉鏈接。
HDFS文件寫入的過程:
過程描述:
寫文件過程分析:
(1) 客戶端經過對DistributedFileSystem對象調用create()函數來新建文件。
(2) 分佈式文件系統對namenod建立一個RPC調用,在文件系統的命名空間中新建一個文件。
(3)Namenode對新建文件進行檢查無誤後,分佈式文件系統返回給客戶端一個FSDataOutputStream對象,FSDataOutputStream對象封裝一個DFSoutPutstream對象,負責處理namenode和datanode之間的通訊,客戶端開始寫入數據。
(4)FSDataOutputStream將數據分紅一個一個的數據包,寫入內部隊列「數據隊列」,DataStreamer負責將數據包依次流式傳輸到由一組namenode構成的管線中。
(5)DFSOutputStream維護着確認隊列來等待datanode收到確認回執,收到管道中全部datanode確認後,數據包從確認隊列刪除。
(6)客戶端完成數據的寫入,對數據流調用close()方法。
(7)namenode確認完成。
namenode如何選擇在那個datanode存儲複本?
須要對可靠性,寫入帶寬和讀取帶寬進行權衡。默認佈局是:在運行客戶端的節點上放第一個複本(若是客戶端運行在集羣以外,則在避免挑選存儲太滿或太忙的節點的狀況下隨機選擇一個節點。)第二個複本放在與第一個不一樣且隨機另外選擇的機架中節點上。第三個複本與第二個複本放在同一個機架上,且隨機選擇另外一個節點。其它複本放在集羣中隨機選擇的節點中,儘可能避免在同一個機架上放太多複本。
一個複本個數爲3的集羣放置位置如圖:
HDFS一致性:HDFS在寫數據務必要保證數據的一致性與持久性,目前HDFS提供的兩種兩個保證數據一致性的方法 hsync()方法和hflush()方法。
hflush: 保證flush的數據被新的reader讀到,可是不保證數據被datanode持久化。
hsync: 與hflush幾乎同樣,不一樣的是hsync保證數據被datanode持久化。
深刻hsync()和hflush()參考兩篇博客
http://www.cnblogs.com/foxmailed/p/4145330.html
http://www.cnblogs.com/yangjiandan/p/3540498.html
七、經過Flume和Sqoop導入數據
能夠考慮使用一些現成的工具將數據導入。
Apache Fluem是一個將大規模流數據導入HDFS的工具。典型應用是從另一個系統中收集日誌數據並實如今HDFS中的彙集操做以便用於後期的分析操做。
Apache Sqoop用來將數據從結構化存儲設備批量導入HDFS中,例如關係數據庫。Sqoop應用場景是組織將白天生產的數據庫中的數據在晚間導入Hive數據倉庫中進行分析。
八、經過distcp並行複製
distcp分佈式複製程序,它從Hadoop文件系統間複製大量數據,也能夠將大量的數據複製到Hadoop。
典型應用場景是在HDFS集羣之間傳輸數據。
% hadoop distcp hdfs://namenode1/foo hdfs://namenode2/bar
九、Hadoop存檔
HDFS中每一個文件均按塊方式存儲,每一個塊的元數據存儲在namenode的內存中,所以Hadoop存儲小文件會很是低效。由於大量的小文件會耗盡namenode中的大部份內存。Hadoop的存檔文件或HAR文件,將文件存入HDFS塊,減小namenode內存使用,容許對文件進行透明地訪問。
Hadoop存檔是經過archive工具根據一組文件建立而來的。運行archive指令:
% hadoop archive -archiveName files.har /my/files /my
列出HAR文件的組成部分:
% hadoop fs -ls /my/files.har
files.har是存檔文件的名稱,這句指令存儲 HDFS下/my/files中的文件。
HAR文件的組成部分:兩個索引文件以及部分文件的集合。
存檔的不足:
新建一個存檔文件會建立原始文件的一個副本,所以須要與要存檔的文件容量相同大小的磁盤空間。
一旦存檔文件,不能從中增長或刪除文件。