HDFS,全稱是Hadoop Distributed Filesystem,是一個分佈式的文件系統,以流式數據訪問模式來存儲超大文件(一次寫入、屢次讀取)。java
HDFS具備例如如下設計特性:node
(1)處理超大文件,指的是GB、TB、PB級別的文件。百度、淘寶都有PB級別的HDFS,百度應該有國內最大規模的HDFS。幾十PB。apache
(2)流式數據訪問。一次寫入,屢次讀取,所處理的場景中,讀取整個數據的延遲比讀取第一條記錄的時間延遲重要。緩存
(3)執行在普通商用PC就能夠,比方3萬級別的普通PCserver(16-32G ECC內存,8-16核CPU)。tcp
(4)是爲高數據吞吐量優化的,以高時間延遲爲代價。分佈式
(5)推薦處理大量小文件。由於namenode將文件系統的元數據存儲在內存中,故文件總數受制於namenode節點內存。依據經驗。一個文件/文件夾/block大約佔用150本身,因此億級別文件還可以,10億級別內存就不夠了。工具
(6)對於寫入。僅僅能有一個寫入操做,也僅僅能把內容加入在文件的末尾。oop
概念:佈局
(1)數據塊(block)。默認64M,通常用128M,相對於文件系統塊(幾K字節大小)、磁盤塊(通常512畢節),HDFS的塊設計明顯大的多。這是爲了最小爲尋址開銷(尋址佔傳輸的百分比,比方:尋址10S。傳輸100MB/S。則尋址時間僅佔傳輸時間的1%)。性能
(2)名稱節點(namenode),是管理者,維護整個HDFS的文件系統樹及樹內所有的文件和文件夾。
(3)數據節點(datanode)。是文件系統工做節點,依據namenode調度,存儲並檢索數據塊,按期向namenode發送它所存儲的塊列表。
namenode單點風險的2種解決的方法:
(1)備份那些組成文件系統元數據持久狀態的文件,比方。持久狀態寫入本地磁盤的同一時候,寫入一個遠程的文件系統。
(2)執行一個輔助namecode,由於輔助namecode的滯後性,因此namecode損壞時,不免會丟失部分數據。
Hadoop有一個抽象文件系統,由org.apache.hadoop.fs.FileSystem定義,HDSF僅僅是當中的一個實現。
Hadoop所實現的文件系統列表。大體例如如下圖所看到的:
Hadoop對文件系統提供了不少接口,它通常使用URI方案來選取合適的文件系統實例進行交互。。比方例如如下代碼:
String uri = "hdfs:///test/input/t/temperature.txt";
Configuration conf = new Configuration();
FileSystem fs = FileSystem.get(URI.create(uri), conf);
即時依據hdsfs://來推斷,使用hdfs.DistributedFileSystem。假設改爲file://,則使用fs.LocalFileSystem。
查看所有命令:
hadoop fs -help
hdfs fsck -help
也可以經過Web界面瀏覽文件系統:http://192.168.1.10:50070/
import java.io.InputStream; import java.net.URL; import org.apache.hadoop.fs.FsUrlStreamHandlerFactory; import org.apache.zookeeper.common.IOUtils; public class URLCat { static { URL.setURLStreamHandlerFactory(new FsUrlStreamHandlerFactory()); } public static void main(String[] args) throws Exception { InputStream in = null; try { in = new URL("hdfs:///test/input/t/temperature.txt").openStream(); IOUtils.copyBytes(in, System.out, 4096, false); } finally { IOUtils.closeStream(in); } } }
以上的方法是很是easy的,利用的java.net.URL對象打開數據流。從中讀取數據。但是這種方法有個限制,Java虛擬機僅僅能調用這個set方法一次,這個限制意味着假設有其它不受控制的第三方組件(已經聲明瞭URLStreamHandlerFactory實例),則咱們沒法再使用這樣的方法讀取數據。於是,不推薦使用。
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.zookeeper.common.IOUtils; public class FileSystemCat { public static void main(String[] args) throws Exception { String uri = "hdfs:///test/input/t/temperature.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, 4096, false); } finally { IOUtils.closeStream(in); } } }實際上,open方法返回的是FSDataInputStream對象,是繼承java.io.DataInputStream的一個特殊類。支持隨機訪問,由此可以從流的任何位置讀取數據。比方,咱們把try段的代碼變成:
in = fs.open(new Path(uri)); IOUtils.copyBytes(in, System.out, 4096, false); ((FSDataInputStream) in).seek(0); //go back to the start of the file IOUtils.copyBytes(in, System.out, 4096, false)則,會顯示兩遍文件temperature.txt文件的內容。
import java.io.BufferedInputStream; import java.io.FileInputStream; 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 = "/home/hadoop/temperature.txt"; String dst = "hdfs:///test/input/t/temperature2.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.println("."); } }); IOUtils.copyBytes(in,out,4096,true); } }
import java.net.URI; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.fs.FileStatus; import org.apache.hadoop.fs.FileSystem; import org.apache.hadoop.fs.FileUtil; import org.apache.hadoop.fs.Path; public class ListStatus { public static void main(String[] args) throws Exception { String uri = "hdfs:///test/input/t/"; Configuration conf = new Configuration(); FileSystem fs = FileSystem.get(URI.create(uri), conf); // 顯示一組路徑的文件夾列表的並集 /* * Path[] paths = new Path[] { new Path("hdfs:///test/input/t/"), new * Path("hdfs:///test/input/wc") }; FileStatus[] status = * fs.listStatus(paths); */ // 通配方式 FileStatus[] status = fs.globStatus(new Path( "hdfs:///test/input/wc/*02.txt")); Path[] listedPaths = FileUtil.stat2Paths(status); for (Path p : listedPaths) { System.out.println(p); } } }
文件元數據FileStatus,封裝了文件系統中文件和文件夾的元數據,包含文件長度、塊大小、備份、改動時間、所有者以及權限信息。
使用FileSystem的delete()方法可以永久性刪除文件或文件夾。
public boolean delete(Path f,boolean recursive) throws IOException
假設f是一個文件或空文件夾,那麼recursive的值就會被忽略。
假設f是一個非空文件夾,則僅僅有recursive爲true才幹刪除。不然會拋出IOException。
複本的佈局策略(以3個爲例):一、執行client的節點,二、離架節點,三、2所在機架的隨機節點。
文件系統的一致模型,描寫敘述了對文件讀/寫的數據可見性。HDFS爲性能犧牲了一些POSIX要求,所以一些操做與你指望的不一樣。
Path p = new Path("p"); Fs.create(p);
Path p = new Path("p"); OutputStream out = fs.create(p); out.write("content".getBytes("UTF-8")); out.flush();
這個一致模型相應用設計的重要性:
假設不調用sync(),可能因client故障而丟失數據。而經常調用sync()也會有額外性能開銷,因此需要在數據健壯性和吞吐量直接有所取捨,這與詳細的應用有關,經過設置不一樣的調用sync()的頻率來衡量應用的性能。終於找到一個合適的頻率。
前面介紹的都是單線程的HDFS訪問模型,distcp是一個分佈式的複製程序。典型應用是在兩個HDFS直接傳輸 數據,假設兩個集羣執行一樣版本號的Hadoop,則可以例如如下:
hadoop distcp hdfs://集羣1的某節點/foo hdfs://集羣2的某節點/foo
可以經過-overrite,指定覆蓋現有的文件;經過-update指定僅更新改動過的文件。
假設兩個集羣版本號不同,可以例如如下這樣:
hadoop distcp hftp://集羣1的某節點:50070/foo hdfs://集羣2的某節點/foo
默認狀況下。每個集羣節點(tasktracker)。最多分配20個map任務,假設複製1000G數據到100個節點的集羣,一共會有2000個map任務,每個map任務平均分配512M數據。可以指定-m參數,下降map任務數,比方-m 1000,將分配1000個map任務,平均每個複製1GB數據;但是通常不推薦這麼作,可能致使集羣不平衡。
Hadoop存檔文件(HAR文件),是一個高效的文件存檔工具,它將文件存入HDFS塊。下降namenode內存使用的同一時候,依舊贊成對文件進行透明的訪問(即Hadoop文檔可以做爲MapReduce的輸入)。
命令演演示樣例如如下:
hadoop fs -ls -R /test/input
hadoop archive -archiveName files.har -p /test input /test/file
hadoop fs -ls /test/file
hadoop fs -ls /test/file/files.har
hadoop fs -ls -R har:///test/file/files.har
hadoop fs -rm -r /test/file
har文件的不足:
(1)建立一個存檔文件會建立原始文件的一個複本。所以需要額外的和原始文件同樣大小的磁盤空間。固然,建立了存檔文件。可以刪除原始文件。
har是不壓縮的,很相似於tar文件
(2)一旦建立,存檔文件不能改動。其實。通常不會改動存檔文件,因爲它們是按期成批存檔的,比方每日或每週。
(3)Har文件做爲mapreduce輸入時,InputFormat類並不知道文件已經存檔,雖然該類可以將多個文件打包成一個MapReduce分片。因此即便在har文件裏處理不少小文件,依舊和原來同樣低效。