1. 程式人生 > >初識Hadoop-HDFS

初識Hadoop-HDFS

原文地址

https://blog.csdn.net/zhruixuan/article/details/85859158

 

HDFS-Hadoop Distributed Filesystem是Hadoop自帶的一個分散式檔案系統,可以將資料儲存在分散的資料節點上。

適用場景

  1. 儲存超大檔案
  2. 流式資料訪問,適合一次寫入,多次讀取,每次讀取都讀取全部資料或大部分資料的場景。

不適用場景

  1. 低時間延遲的資料訪問,HDFS適合高吞吐的訪問模式,傳統的關係型資料庫的訪問方式並不適合HDFS,要求低延遲的場景可以使用Hbase。
  2. 大量的小檔案,因為namenode將檔案的元資料儲存在記憶體中,因此該檔案系統所能儲存的檔案總量受限於namenode節點的記憶體容量。一般來說,每個檔案,目錄以及資料塊所佔用的記憶體是150位元組,那麼儲存100萬個檔案,假設每個檔案佔用一個數據塊,那麼至少需要1502100萬字節(300M)的記憶體。
  3. 多使用者寫入,任意修改檔案,HDFS中的檔案的寫入只支援單個寫入者,而且寫操作是以只新增的方式在檔案末尾寫入。不支援多個寫入者,也不支援在檔案的任意位置進行修改。

重要概念

資料塊

對於磁碟來說有預設的資料塊大小(一般512位元組),這是磁碟進行讀寫的最小單位,構建於單個磁碟之上的檔案系統可以通過磁碟塊來管理儲存的資料,該檔案系統的資料塊大小可以是磁碟塊的整數倍。HDFS中也有資料塊的概念,預設是128M(設計的比較大的原因是為了減少定址開銷),但是HDFS中小於一個塊大小的檔案不會佔據整個塊的空間。

namenode、datanode

HDFS系統中有兩類節點-管理節點(namenode),工作節點(datanode),namenode只有一個處於執行狀態,工作節點有多個。

  1. namenode 負責管理檔案系統的名稱空間,它維護著檔案系統樹以及整棵樹內所有的檔案和目錄。儲存檔案到檔案資料塊的對映關係。這些資訊以名稱空間映象檔案編輯日誌檔案的形式永久儲存在本地磁碟上。namenode也記錄著每個檔案的各個資料塊所在的資料節點(datanode)資訊,但是它並不永久儲存這類資訊,因為這些資訊會在系統啟動時根據資料節點發來的資訊重建。
  2. datanode是檔案系統的工作節點,它們負責儲存和檢索資料塊,並定期向namenode報告它們所儲存的資料塊列表。
  3. namenode容錯,如果namenode損壞,整個HDFS將不可用,因為檔案系統樹儲存在namenode,如果沒有這些資訊就無法根據datanode的檔案塊資訊進行檔案重建。所以需要對namenode進行容錯處理。一般有兩種方式:
    1. 備份namenode上關於檔案系統元資料的檔案。Hadoop可以通過配置使namenode在多個檔案系統上儲存元資料的持久檔案。並且這些寫操作是原子的。一般的配置是,將持久狀態寫入本地磁碟的同時,寫入一個遠端掛載的網路檔案系統(NFS)。
    2. 執行一個輔助的namenode,這個節點負責定期合併主namenode上的名稱空間映象檔案編輯日誌檔案以保持和主節點同步,當主節點發生故障時啟用。但是這樣也難免丟失部分資料,所以一般是將NFS中的持久化資料複製到輔助namenode。

塊快取

通常datanode從磁碟中讀取檔案塊。但是對於頻繁訪問的檔案對應的資料塊可能被快取在datanode的記憶體中。

聯邦HDFS(擴充套件namenode)

namenode在記憶體中儲存每個檔案和每個資料塊的引用關係。所以namenode的記憶體將成為系統橫向擴充套件的瓶頸。在2.X版本中引入的聯邦HDFS允許系統新增namenode實現擴充套件,其中每個namenode管理檔案系統名稱空間的一部分。例如一個namenode管理/user目錄下的所有檔案,另一個管理/order下的所有檔案。

命令

  1. hadoop fs -help

  2. 拷貝本地檔案到HDSF: hadoop fs -copyFromLocal input/docs/foo.txt (hdfs://localhost/user/tom/)foo.txt

  3. 拷貝HDFS檔案到本地並校驗是否更改: hadoop fs -copyToLocal (hdfs://localhost/user/tom/)foo.txt foo.txt md5 input/docs/foo.txt foo.txt

  4. 建立目錄 hadoop fs -mkdir books

  5. 列出目錄檔案 hadoop fs -ls drwxr-xr-x - tom supergroup 0 2019-1-5 19:35 books drwxr-xr-x 1 tom supergroup 119 2019-1-5 19:35 foo.txt 第1列是檔案模式,第2列是這個檔案的備份數(目錄沒有複本的概念,目錄作為元資料儲存在namenode中,而非datanode中),第3列第4列顯示檔案的所屬使用者和組別,第5列顯示檔案大小(位元組)

  6. distcp並行複製檔案(通過mapreduce實現,只有maper沒有reducer),典型用法是在兩個hdfs叢集間複製資料,例如,以下命令在第二個叢集上為第一個叢集foo目錄建立了一個備份: hadoop distcp -update -delete -p hdfs://namenode1/foo hdfs://namenode2/foo distcp

Hadoop檔案系統

HDFS只是Hadoop檔案系統的一種實現,java抽象類org.apache.hadoop.fs.FileSystem定義了Hadoop檔案系統的客戶端藉口,以下是其幾個典型實現。 hadoop檔案系統-1 hadoop檔案系統-2 可以通過uri選擇合適的檔案系統,例如命令列中列出本地檔案系統根目錄下所有檔案 hadoop fs -ls file:/// 如果非java應用想要訪問HDFS可以通過http或者https直接訪問或者通過代理 http訪問HDFS direct access方式需要HDFS的節點作為http伺服器響應請求(dfs.webhdfs.enabled設定為true)

java介面FileSystem

URL方式訪問資料

  1. public class URLCat{
  2. static{
  3. //為了讓URL可以識別"hdfs"的uri
  4. //每個Jvm只能呼叫一次該方法
  5. URL.setURLStreamHandlerFactory(new FsUrlStreamHandlerFactory());
  6. }
  7. public static void main(String[] args) trows Exception{
  8. InputStream in = null;
  9. try{
  10. in = new URL(args[0]).openStream();
  11. //process in
  12. IOUtils.copyBytes(in, System.out, 4096, false);
  13. } finally{
  14. IOUtils.closeStream(in);
  15. }
  16. }
  17. }

執行

  1. % export HADOOP_CLASSPATH=hadoop-examples.jar
  2. % hadoop URLCat hdfs://localhost/user/tom/foo.txt

FileSystem Api方式訪問資料

因為有時候setURLStreamHandlerFactory已經被其他元件呼叫了,我們無法再設定,所以可以使用FileSystem Api的方式訪問資料。 Hadoop中通過Path物件而不是File物件來表示檔案 讀取

  1. public class FileSystemCat{
  2. public static void main(String[] args) throws Exception{
  3. String uri = args[0];
  4. Configuration conf = new Configuration();
  5. FileSystem fs = FileSystem.get(URI.create(uri), conf);
  6. InputStream in = null;
  7. try{
  8. //不指定緩衝區,使用預設緩衝區4K
  9. in = fs.open(new Path(uri));
  10. IOUtils.copyBytes(in, System.out, 4096, false);
  11. //回到起點,seek操作開銷比較高
  12. in.seek(0);
  13. IOUtils.copyBytes(in, System.out, 4096, false);
  14. }finally{
  15. IOUtils.closeStream(in);
  16. }
  17. }
  18. }

寫入

  1. public class FileCopyWithProgress{
  2. public static void main(String[] args) throws Exception{
  3. String localSrc = args[0];
  4. String dst = args[1];
  5. InputStream in = new BufferedInputStream(new FileInputSream(localSrc));
  6. Configuration conf = new Configuration();
  7. FileSystem fs = FileSystem.get(URI.create(dst), conf);
  8. OutputStream out = fs.create(new Path(dst), new Progressable(){
  9. //反饋進度
  10. public void progress(){
  11. System.out.print(".");
  12. }
  13. });
  14. IOUtils.copyBytes(in, out, 4096, true);
  15. }
  16. }
  1. % hadoop FileCopyWithProgress input/docs/foo.txt hdfs://localhost/user/tom/foo.txt

列出一組hdfs一組路徑中的檔案資訊

  1. public class listStatus{
  2. public static void main(String[] args) throws Exception{
  3. String uri = args[0];
  4. Configuration conf = new Configuration();
  5. FileSystem fs = FileSystem.get(URI.create(uri), conf);
  6. Path[] paths = new Path[args.length];
  7. for(int i = 0; i<paths.length; i++){
  8. paths[i] = new Path(args[i]);
  9. }
  10. FileStatus[] status = fs.listStatus(paths);
  11. //將FileStatus轉換為Path
  12. Path[] listedPaths = FileUtil.stat2Paths(status);
  13. for(Path p : listedPaths){
  14. System.out.println(p);
  15. }
  16. }
  17. }

資料流

讀取資料流

客戶端讀取hdfs資料

寫入資料流

寫入hdfs資料流

一致模型

檔案系統的一致模型描述了檔案讀/寫資料的可見性,HDFS為效能犧牲了一些POSIX的要求。 新建一個檔案後可以在檔案系統的名稱空間中立即可見:

  1. Path p = new Path("p");
  2. Fs.create(p);
  3. assertThat(fs.exists(p), is(true));

但是,寫入檔案的內容並不保證能立即可見: 當寫入的資料超過一個塊後,第一個資料塊對新的reader就是可見的。之後的塊也不例外,總之,當前正在寫入的塊對其他reader不可見。

  1. Path p = new Path("p");
  2. OutputStream out = fs.create(p);
  3. out.write("content".getBytes("UTF-8"));
  4. out.flush();
  5. assertThat(fs.getFileStatus(p).getLen(), is(0L));

HDFS提供了一種強行將所有快取重新整理到datanode中的手段,即對FSDataOutputStream呼叫hflush()方法。當hflush()方法返回成功後,對所有新的reader而言,HDFS能保證檔案中到目前為止寫入的資料均到達所有datanode的寫入管道並對所有新的reader均可見。

  1. Path p = new Path("p");
  2. FSDataOutputStream out = fs.create(p);
  3. out.write("content".getBytes("UTF-8"));
  4. out.hflush();
  5. assertThat(fs.getFileStatus(p).getLen(), is((long)"content".getBytes("UTF-8")));

在hdfs中關閉檔案(out.close())其實隱含了執行hflush()

需要注意的是,hflush()不能保證datanode已經將資料寫到磁碟上,僅確保資料在datanode的記憶體中(如果斷電,資料可能丟失),為確保資料寫入磁碟,可以用hsync()