1. 程式人生 > >HDFS知識點彙總

HDFS知識點彙總

一.Hdfs簡介

hdfs是一個檔案系統,用於儲存檔案,通過統一的名稱空間——目錄樹來定位檔案,並且是分散式的,由很多伺服器聯合起來實現其功能,叢集中的伺服器各自負責角色;

角色:

 HDFS的三個節點:Namenode,Datanode,Secondary Namenode

Namenode:HDFS的守護程序,用來管理檔案系統的名稱空間,負責記錄檔案是如何分割成資料塊,以及這些資料塊分別被儲存到那些資料節點上,它的主要功能是對記憶體及IO進行集中管理。

Datanode:檔案系統的工作節點,根據需要儲存和檢索資料塊,並且定期向namenode傳送他們所儲存的塊的列表。

Secondary Namenode:輔助後臺程式,與NameNode進行通訊,以便定期儲存HDFS元資料的快照。

重要特徵:

  1.HDFS中的檔案在物理上是分塊儲存(block),塊的大小可以通過配置引數( dfs.blocksize)來規定,預設大小在hadoop2.x版本中是128M,老版本中是64M

使用資料塊的好處是:

(1)一個檔案的大小可以大於網路中任意一個磁碟的容量。檔案的所有塊不需要儲存在同一個磁碟上,因此它們可以 利用 叢集上的任意一個磁碟進行儲存。

(2)簡化了儲存子系統的設計,將儲存子系統控制單元設定為塊,可簡化儲存管理,同時元資料就不需要和塊一同存 儲,  用一個單獨的系統就可以管理這些塊的元資料。

(3)資料塊適合用於資料備份進而提供資料容錯能力和提高可用性。

   2.HDFS檔案系統會給客戶端提供一個統一的抽象目錄樹,客戶端通過路徑來訪問檔案,形如:hdfs://namenode:port/dir-a/dir-b/dir-c/file.data

   3.目錄結構及檔案分塊資訊(元資料)的管理由namenode節點承擔——namenode是HDFS叢集主節點,負責維護整個hdfs檔案系統的目錄樹,以及每一個路徑(檔案)所對應的block塊資訊(block的id,及所在的datanode伺服器)

     4.檔案的各個block的儲存管理由datanode節點承擔---- datanode是HDFS叢集從節點,每一個block都可以在多個datanode上儲存多個副本(副本數量也可以通過引數設定dfs.replication)

     5.HDFS是設計成適應一次寫入,多次讀出的場景,且不支援檔案的修改

二.Hdfs的shell(命令列客戶端)常用命令

 Hdfs提供shell命令列客戶端,使用方法如下:

常用命令引數介紹:

-help            

功能:輸出這個命令引數手冊

-ls                 

功能:顯示目錄資訊

示例: hadoop fs -ls hdfs://hadoop-server01:9000/

備註:這些引數中,所有的hdfs路徑都可以簡寫

-->hadoop fs -ls /   等同於上一條命令的效果

-mkdir             

功能:在hdfs上建立目錄

示例:hadoop fs  -mkdir  -p  /aaa/bbb/cc/dd

-moveFromLocal           

功能:從本地剪下貼上到hdfs

示例:hadoop  fs  - moveFromLocal  /home/hadoop/a.txt  /aaa/bbb/cc/dd

-moveToLocal             

功能:從hdfs剪下貼上到本地

示例:hadoop  fs  - moveToLocal   /aaa/bbb/cc/dd  /home/hadoop/a.txt

--appendToFile 

功能:追加一個檔案到已經存在的檔案末尾

示例:hadoop  fs  -appendToFile  ./hello.txt  hdfs://hadoop-server01:9000/hello.txt

可以簡寫為:

Hadoop  fs  -appendToFile  ./hello.txt  /hello.txt

 

-cat 

功能:顯示檔案內容 

示例:hadoop fs -cat  /hello.txt

 

-tail                

功能:顯示一個檔案的末尾

示例:hadoop  fs  -tail  /weblog/access_log.1

-text                 

功能:以字元形式列印一個檔案的內容

示例:hadoop  fs  -text  /weblog/access_log.1

-chgrp

-chmod

-chown

功能:linux檔案系統中的用法一樣,對檔案所屬許可權

示例:

hadoop  fs  -chmod  666  /hello.txt

hadoop  fs  -chown  someuser:somegrp   /hello.txt

-copyFromLocal   

功能:從本地檔案系統中拷貝檔案到hdfs路徑去

示例:hadoop  fs  -copyFromLocal  ./jdk.tar.gz  /aaa/

-copyToLocal     

功能:從hdfs拷貝到本地

示例:hadoop fs -copyToLocal /aaa/jdk.tar.gz

-cp             

功能:從hdfs的一個路徑拷貝hdfs的另一個路徑

示例: hadoop  fs  -cp  /aaa/jdk.tar.gz  /bbb/jdk.tar.gz.2

 

-mv                    

功能:在hdfs目錄中移動檔案

示例: hadoop  fs  -mv  /aaa/jdk.tar.gz  /

-get             

功能:等同於copyToLocal,就是從hdfs下載檔案到本地

示例:hadoop fs -get  /aaa/jdk.tar.gz

-getmerge            

功能:合併下載多個檔案

示例:比如hdfs的目錄 /aaa/下有多個檔案:log.1, log.2,log.3,...

hadoop fs -getmerge /aaa/log.* ./log.sum

-put               

功能:等同於copyFromLocal

示例:hadoop  fs  -put  /aaa/jdk.tar.gz  /bbb/jdk.tar.gz.2

 

-rm               

功能:刪除檔案或資料夾

示例:hadoop fs -rm -r /aaa/bbb/

 

-rmdir                

功能:刪除空目錄

示例:hadoop  fs  -rmdir   /aaa/bbb/ccc

-df              

功能:統計檔案系統的可用空間資訊

示例:hadoop  fs  -df  -h  /

 

-du

功能:統計資料夾的大小資訊

示例:

hadoop  fs  -du  -s  -h /aaa/*

 

-count        

功能:統計一個指定目錄下的檔案節點數量

示例:hadoop fs -count /aaa/

 

-setrep               

功能:設定hdfs中檔案的副本數量

示例:hadoop fs -setrep 3 /aaa/jdk.tar.gz

 

三.Hdfs的工作機制

1.概述

       (1)HDFS叢集分為兩大角色:NameNode、DataNode

    (2)NameNode負責管理整個檔案系統的元資料

    (3)DataNode 負責管理使用者的檔案資料塊

    (4)檔案會按照固定的大小(blocksize)切成若干塊後分布式儲存在若干臺datanode上

    (5)每一個檔案塊可以有多個副本,並存放在不同的datanode上

    (6)Datanode會定期向Namenode彙報自身所儲存的檔案block資訊,而namenode則會負責保持檔案的副本數量

    (7)HDFS的內部工作機制對客戶端保持透明,客戶端請求訪問HDFS都是通過向namenode申請來進行

2.HDFS寫資料流程

   (1)客戶端要向HDFS寫資料,首先要跟namenode通訊以確認可以寫檔案並獲得接收檔案block的datanode,然後,客戶端按順序將檔案逐個block傳遞給相應datanode,並由接收到block的datanode負責向其他datanode複製block的副本

    

  (2)詳細步驟解析

    1.根namenode通訊請求上傳檔案,namenode檢查目標檔案是否已存在,父目錄是否存在

    2.namenode返回是否可以上傳

    3.client請求第一個 block該傳輸到哪些datanode伺服器上

    4.namenode返回3個datanode伺服器ABC

    5.client請求3臺dn中的一臺A上傳資料(本質上是一個RPC呼叫,建立pipeline),A收到請求會繼續呼叫B,然後B呼叫                C,將真個pipeline建立完成,逐級返回客戶端

    6.client開始往A上傳第一個block(先從磁碟讀取資料放到一個本地記憶體快取),以packet為單位,A收到一個packet就                 會傳給B,B傳給C;A每傳一個packet會放入一個應答佇列等待應答

    7.當一個block傳輸完成之後,client再次請求namenode上傳第二個block的伺服器。

3.HDFS讀資料流程

    (1)客戶端將要讀取的檔案路徑傳送給namenode,namenode獲取檔案的元資訊(主要是block的存放位置資訊)返回給客戶端,客戶端根據返回的資訊找到相應datanode逐個獲取檔案的block並在客戶端本地進行資料追加合併從而獲得整個檔案

    

    (2)詳細步驟解析

      1.跟namenode通訊查詢元資料,找到檔案塊所在的datanode伺服器

      2.挑選一臺datanode(就近原則,然後隨機)伺服器,請求建立socket流

      3.datanode開始傳送資料(從磁盤裡面讀取資料放入流,以packet為單位來做校驗)

      4.客戶端以packet為單位接收,現在本地快取,然後寫入目標檔案

四.Namenode工作機制

  1.Namenode工作職責:負責客戶端請求的響應,元資料的管理(查詢,修改)

  2.元資料管理 : namenode對資料的管理採用了三種儲存形式:記憶體元資料(NameSystem)  磁碟元資料映象檔案資料操作日誌檔案(可通過日誌運算出元資料)

  3.元資料儲存機制:

  A、記憶體中有一份完整的元資料(記憶體meta data)

  B、磁碟有一個“準完整”的元資料映象(fsimage)檔案(在namenode的工作目錄中)

  C、用於銜接記憶體metadata和持久化元資料映象fsimage之間的操作日誌(edits檔案注:當客戶端對hdfs中的檔案進行新                增或者修改操作,操作記錄首先被記入edits日誌檔案中,當客戶端操作成功後,相應的元資料會更新到記憶體meta.data

  4.元資料的checkpoint:每隔一段時間,會由secondary namenode將namenode上積累的所有edits和一個最新的fsimage下              載到本地,並載入到記憶體進行merge(這個過程稱為checkpoint)

    

    checkpoint操作的觸發條件配置引數:

      dfs.namenode.checkpoint.check.period=60  #檢查觸發條件是否滿足的頻率,60秒

      dfs.namenode.checkpoint.dir=file://${hadoop.tmp.dir}/dfs/namesecondary

      #以上兩個引數做checkpoint操作時,secondary namenode的本地工作目錄

      dfs.namenode.checkpoint.edits.dir=${dfs.namenode.checkpoint.dir}

      dfs.namenode.checkpoint.max-retries=3  #最大重試次數

      dfs.namenode.checkpoint.period=3600  #兩次checkpoint之間的時間間隔3600秒

      dfs.namenode.checkpoint.txns=1000000 #兩次checkpoint之間最大的操作記錄

    checkpoint的附帶作用

      namenode和secondary namenode的工作目錄儲存結構完全相同,所以,當namenode故障退出需要重新恢復時,                    可以從secondary namenode的工作目錄中將fsimage拷貝到namenode的工作目錄,以恢復namenode的元資料.

五.Datanode工作機制

  1.Datanode工作職責:儲存管理使用者的檔案塊資料,定期向namenode彙報自身所持有的block資訊(通過心跳資訊上報)    

<property>

           <name>dfs.blockreport.intervalMsec</name>

           <value>3600000</value>

           <description>Determines block reporting interval in milliseconds. </description>

</property>

  2.Datanode掉線判斷時限引數

   datanode程序死亡或者網路故障造成datanode無法與namenode通訊,namenode不會立即把該節點判定為死亡,要經過一段時間,這段時間暫稱作超時時長。HDFS預設的超時時長為10分鐘+30秒。如果定義超時時間為timeout,則超時時長的計算公式為:timeout  = 2 * heartbeat.recheck.interval + 10 * dfs.heartbeat.interval。 而預設的heartbeat.recheck.interval 大小為5分鐘,dfs.heartbeat.interval預設為3秒。

        需要注意的是hdfs-site.xml 配置檔案中的heartbeat.recheck.interval的單位為毫秒,dfs.heartbeat.interval的單位為秒。所以,舉個例子,如果heartbeat.recheck.interval設定為5000(毫秒),dfs.heartbeat.interval設定為3(秒,預設),則總的超           時時間為40秒。  

<property>

           <name>heartbeat.recheck.interval</name>

           <value>2000</value>

</property>

<property>

          <name>dfs.heartbeat.interval</name>

          <value>1</value>

</property>

六. Hdfs的java操作

1.搭建開發環境

    (1)引入依賴     

 <dependency>

          <groupId>org.apache.hadoop</groupId>

          <artifactId>hadoop-client</artifactId>

          <version>2.6.1</version>

 </dependency>

    (2)window下開發的說明  建議在linux下進行hadoop應用的開發,不會存在相容性問題。如在window上做客戶端應用開發,需要設定以下環境:

      A、在windows的某個目錄下解壓一個hadoop的安裝包

      B、將安裝包下的lib和bin目錄用對應windows版本平臺編譯的本地庫替換

      C、在window系統中配置HADOOP_HOME指向你解壓的安裝包

      D、在windows系統的path變數中加入hadoop的bin目錄

2.HDFS客戶端操作資料程式碼示例:

(1) 檔案的增刪改查

public class HdfsClient {

         FileSystem fs = null;

         @Before
         public void init() throws Exception {

                   // 構造一個配置引數物件,設定一個引數:我們要訪問的hdfs的URI
                   // 從而FileSystem.get()方法就知道應該是去構造一個訪問hdfs檔案系統的客戶端,以及hdfs的訪問地址
                   // new Configuration();的時候,它就會去載入jar包中的hdfs-default.xml
                   // 然後再載入classpath下的hdfs-site.xml
                   Configuration conf = new Configuration();
                   conf.set("fs.defaultFS", "hdfs://hdp-node01:9000");
                   /**
                    * 引數優先順序: 1、客戶端程式碼中設定的值 2、classpath下的使用者自定義配置檔案 3、然後是伺服器的預設配置
                    */
                   conf.set("dfs.replication", "3");

                   // 獲取一個hdfs的訪問客戶端,根據引數,這個例項應該是DistributedFileSystem的例項
                   // fs = FileSystem.get(conf);

                   // 如果這樣去獲取,那conf裡面就可以不要配"fs.defaultFS"引數,而且,這個客戶端的身份標識已經是hadoop使用者
                   fs = FileSystem.get(new URI("hdfs://hdp-node01:9000"), conf, "hadoop");

         }

         /**
          * 往hdfs上傳檔案
          *
          * @throws Exception
          */
         @Test
         public void testAddFileToHdfs() throws Exception {

                   // 要上傳的檔案所在的本地路徑
                   Path src = new Path("g:/redis-recommend.zip");
                   // 要上傳到hdfs的目標路徑
                   Path dst = new Path("/aaa");
                   fs.copyFromLocalFile(src, dst);
                   fs.close();
         }

         /**
          * 從hdfs中複製檔案到本地檔案系統
          *
          * @throws IOException
          * @throws IllegalArgumentException
          */
         @Test
         public void testDownloadFileToLocal() throws IllegalArgumentException, IOException {
                   fs.copyToLocalFile(new Path("/jdk-7u65-linux-i586.tar.gz"), new Path("d:/"));
                   fs.close();
         }

         @Test
         public void testMkdirAndDeleteAndRename() throws IllegalArgumentException, IOException {

                   // 建立目錄
                   fs.mkdirs(new Path("/a1/b1/c1"));

                   // 刪除資料夾 ,如果是非空資料夾,引數2必須給值true
                   fs.delete(new Path("/aaa"), true);

                   // 重新命名檔案或資料夾
                   fs.rename(new Path("/a1"), new Path("/a2"));

         }

         /**
          * 檢視目錄資訊,只顯示檔案
          *
          * @throws IOException
          * @throws IllegalArgumentException
          * @throws FileNotFoundException
          */
         @Test
         public void testListFiles() throws FileNotFoundException, IllegalArgumentException, IOException {

                   // 思考:為什麼返回迭代器,而不是List之類的容器
                   RemoteIterator<LocatedFileStatus> listFiles = fs.listFiles(new Path("/"), true);

                   while (listFiles.hasNext()) {
                            LocatedFileStatus fileStatus = listFiles.next();
                            System.out.println(fileStatus.getPath().getName());
                            System.out.println(fileStatus.getBlockSize());
                            System.out.println(fileStatus.getPermission());
                            System.out.println(fileStatus.getLen());
                            BlockLocation[] blockLocations = fileStatus.getBlockLocations();
                            for (BlockLocation bl : blockLocations) {
                                     System.out.println("block-length:" + bl.getLength() + "--" + "block-offset:" + bl.getOffset());
                                     String[] hosts = bl.getHosts();
                                     for (String host : hosts) {
                                               System.out.println(host);
                                     }
                            }
                            System.out.println("--------------為angelababy列印的分割線--------------");
                   }
         }

         /**
          * 檢視檔案及資料夾資訊
          *
          * @throws IOException
          * @throws IllegalArgumentException
          * @throws FileNotFoundException
          */
         @Test
         public void testListAll() throws FileNotFoundException, IllegalArgumentException, IOException {

                   FileStatus[] listStatus = fs.listStatus(new Path("/"));

                   String flag = "d--             ";
                   for (FileStatus fstatus : listStatus) {
                            if (fstatus.isFile())  flag = "f--         ";
                            System.out.println(flag + fstatus.getPath().getName());
                   }
         }
}

(2)通過流的方式訪問hdfs

/**
 * 相對那些封裝好的方法而言的更底層一些的操作方式
 * 上層那些mapreduce   spark等運算框架,去hdfs中獲取資料的時候,就是調的這種底層的api
 * @author
 *
 */
public class StreamAccess {
        
         FileSystem fs = null;
 
         @Before
         public void init() throws Exception {
 
                   Configuration conf = new Configuration();
                   fs = FileSystem.get(new URI("hdfs://hdp-node01:9000"), conf, "hadoop");
 
         }
        
        
        
         @Test
         public void testDownLoadFileToLocal() throws IllegalArgumentException, IOException{
                  
                   //先獲取一個檔案的輸入流----針對hdfs上的
                   FSDataInputStream in = fs.open(new Path("/jdk-7u65-linux-i586.tar.gz"));
                  
                   //再構造一個檔案的輸出流----針對本地的
                   FileOutputStream out = new FileOutputStream(new File("c:/jdk.tar.gz"));
                  
                   //再將輸入流中資料傳輸到輸出流
                   IOUtils.copyBytes(in, out, 4096);
                  
                  
         }
        
        
         /**
          * hdfs支援隨機定位進行檔案讀取,而且可以方便地讀取指定長度
          * 用於上層分散式運算框架併發處理資料
          * @throws IllegalArgumentException
          * @throws IOException
          */
         @Test
         public void testRandomAccess() throws IllegalArgumentException, IOException{
                   //先獲取一個檔案的輸入流----針對hdfs上的
                   FSDataInputStream in = fs.open(new Path("/iloveyou.txt"));
                  
                  
                   //可以將流的起始偏移量進行自定義
                   in.seek(22);
                  
                   //再構造一個檔案的輸出流----針對本地的
                   FileOutputStream out = new FileOutputStream(new File("c:/iloveyou.line.2.txt"));
                  
                   IOUtils.copyBytes(in,out,19L,true);
                  
         }
        
        
        
         /**
          * 顯示hdfs上檔案的內容
          * @throws IOException
          * @throws IllegalArgumentException
          */
         @Test
         public void testCat() throws IllegalArgumentException, IOException{
                  
                   FSDataInputStream in = fs.open(new Path("/iloveyou.txt"));
                  
                   IOUtils.copyBytes(in, System.out, 1024);
         }
}