HDFS 教程(超詳細)

1. HDFS 介紹

1.1 HDFS 背景及定義

  1. HDFS 產生背景

      隨着數據量越來越大,在一個操作系統管轄的範圍內存不下了,那麼就分配到更多的操作系統管理的磁盤中,但是不方便管理和維護,迫切需要一種系統來管理多臺機器上的文件,這就是分佈式文件管理系統。HDFS 只是分佈式文件管理系統中的一種。

  2. HDFS 概念

      HDFS(Hadoop Distributed File System),它是一個文件系統,用於存儲文件,通過目錄樹來定位文件;其次,它是分佈式的,由很多服務器聯合起來實現其功能,集羣中的服務器有各自的角色。
      HDFS 的設計適合一次寫入,多次讀出的場景,且不支持文件的修改。適合用來做數據分析,並不適合用來做網盤應用。

1.2 HDFS 的優缺點

  1. 優點

    (1)高容錯性

      ① 數據自動保存多個副本。它通過增加副本的形式,提高容錯性。
      ② 某一個副本丟失以後,它可以自動恢復。

    (2)適合大數據處理

      ① 數據規模:能夠處理數據規模達到 GB、TB、甚至 PB 級別的數據。
      ② 文件規模:能夠處理百萬規模以上的文件數量,數量相當之大。

    (3)流式數據訪問,它能保證數據的一致性。
    (4)可構建在廉價機器上,通過多副本機制,提高可靠性。

  2. 缺點

    (1)不適合低延時數據訪問,比如毫秒級的存儲數據,是做不到的。
    (2)無法高效的對大量小文件進行存儲。

      ① 存儲大量小文件的話,它會佔用 NameNode 大量的內存來存儲文件、目錄和塊信息。這樣是不可取的,因爲 NameNode 的內存總是有限的。
      ② 小文件存儲的尋道時間會超過讀取時間,它違反了 HDFS 的設計目標。

    (3)不支持併發寫入、文件隨機修改

      ① 一個文件只能有一個寫,不允許多個線程同時寫。
      ② 僅支持數據 append(追加),不支持文件的隨機修改。

1.3 HDFS 組成架構

在這裏插入圖片描述

  1. Client: 就是客戶端。

    (1)文件切分。文件上傳 HDFS 的時候,Client 將文件切分成一個一個的 Block,然後進行存儲。
    (2)與 NameNode 交互,獲取文件的位置信息。
    (3)與 DataNode 交互,讀取或者寫入數據。
    (4)Client 提供一些命令來管理 HDFS,比如啓動或者關閉 HDFS。
    (5)Client 可以通過一些命令來訪問 HDFS。

  2. NameNode: 就是 Master,它是一個主管、管理者。

    (1)管理 HDFS 的名稱空間。
    (2)管理數據塊(Block)映射信息。
    (3)配置副本策略。
    (4)處理客戶端讀寫請求。

  3. DataNode: 就是 Slave,NameNode 下達命令,DataNode 執行實際的操作。

    (1)存儲實際的數據塊。
    (2)執行數據塊的讀/寫操作。

  4. SecondaryNameNode: 並非 NameNode 的熱備。當 NameNode 掛掉時,它並不能馬上替換 NameNode 並提供服務。

    (1)輔助 NameNode,分擔其工作量。
    (2)定期合併 Fsimage 和 Edits,並推送給 NameNode。
    (3)在緊急情況下,可輔助恢復 NameNode。

1.4 HDFS 文件塊大小

  1. HDFS 中的文件在物理上是分塊存儲(block),塊的大小可以通過配置參數(dfs.blocksize)來規定,默認大小在 hadoop2.x 版本中是128M,老版本中是 64M。

  2. HDFS 的塊比磁盤的塊大,其目的是爲了最小化尋址開銷。如果塊設置得足夠大,從磁盤傳輸數據的時間會明顯大於定位這個塊開始位置所需的時間。因而,傳輸 一個由多個塊組成的文件的時間取決於磁盤傳輸速率。

  3. 如果尋址時間約爲 10ms,而傳輸速率爲 100MB/s,爲了使尋址時間僅佔傳輸時間的 1%,我們要將塊大小設置約爲 100MB。默認的塊大小 128MB。
    塊的大小:10ms*100*100M/s = 100M

    思考: 爲什麼塊的大小不能設置太小,也不能設置太大?

    (1)HDFS 的塊設置太小,會增加尋址時間,程序一直在找塊的開始位置;
    (2)如果塊的設置太大,從磁盤傳輸的時間會明顯大於定位這個塊開始位置所需的時間。導致程序在處理這塊數據時,會非常慢。

    總結: HDFS 塊大小設置主要取決於磁盤傳輸速率。

2. HDFS 的 Shell 操作

  1. 基本語法

    hadoop fs 具體命令hdfs dfs 具體命令

  2. 命令大全
    在這裏插入圖片描述

  3. 常用命令

命令 說明 舉例
-help 輸出這個命令參數 hadoop fs -help rm
-ls 顯示目錄信息 hadoop fs -ls /
-mkdir 在 HDFS 上創建目錄 hadoop fs -mkdir -p /usr/input
-rmdir 刪除空目錄 hadoop fs -rmdir /test
-rm 刪除文件或文件夾 hadoop fs -rm /usr/input/test.txt
-moveFromLocal 從本地剪切粘貼到 HDFS hadoop fs -moveFromLocal a.txt /usr/input
-copyFromLocal 從本地文件系統中拷貝文件到 HDFS 路徑去 hadoop fs -copyFromLocal c.txt /
-copyToLocal 從 HDFS 拷貝到本地 hadoop fs -copyToLocal /usr/input/a.txt
-appendToFile 追加一個文件到已經存在的文件末尾 hadoop fs -appendToFile b.txt /usr/input/a.txt
-cat 顯示文件內容 hadoop fs -cat /usr/input/a.txt
-cp 從 HDFS 的一個路徑拷貝到 HDFS 的另一個路徑 hadoop fs -cp /usr/input/a.txt /f.txt
-mv 在 HDFS 目錄中移動文件 hadoop fs -mv /f.txt /usr/input/
-get 等同於 copyToLocal hadoop fs -get /usr/input/a.txt
-put 等同於 copyFromLocal hadoop fs -put d.txt /usr/input/
-getmerge 合併下載多個文件 hadoop fs -getmerge /usr/input/* ./tmp.txt
-tail 顯示一個文件的末尾 hadoop fs -tail /usr/input/a.txt
-chgrp 、-chmod、-chown Linux 文件系統中的用法一樣,修改文件所屬權限 hadoop fs -chmod 666 /usr/input/a.txt
hadoop fs -chown lyh:dell /usr/input/a.txt
-du 統計文件夾的大小信息 hadoop fs -du -s -h /usr/input
hadoop fs -du -h /usr/input
-setrep 設置 HDFS 中文件的副本數量 hadoop fs -setrep 10 /usr/input/a.txt

說明: 這裏設置的副本數只是記錄在 NameNode 的元數據中,是否真的會有這麼多副本,還得看DataNode 的數量。因爲目前只有 3 臺設備,最多也就 3 個副本,只有節點數的增加到 10 臺時,副本數才能達到 10。

3. HDFS 客戶端操作

3.1 HDFS 客戶端環境準備

  1. 將 hadoop 的安裝包解壓到本系統到非中文路徑(E:\hadoop\hadoop-2.7.7)

  2. 配置 HADOOP_HOME 環境變量

    在這裏插入圖片描述
    在這裏插入圖片描述

  3. 創建一個 Maven 工程 hdfs,創建一個 module 爲 HdfsClientDemo

  4. 導入相應的依賴座標 + 日誌添加

<dependencies>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.apache.logging.log4j</groupId>
        <artifactId>log4j-core</artifactId>
        <version>2.8.2</version>
    </dependency>
    <dependency>
        <groupId>org.apache.hadoop</groupId>
        <artifactId>hadoop-common</artifactId>
        <version>2.7.7</version>
    </dependency>
    <dependency>
        <groupId>org.apache.hadoop</groupId>
        <artifactId>hadoop-client</artifactId>
        <version>2.7.7</version>
    </dependency>
    <dependency>
        <groupId>org.apache.hadoop</groupId>
        <artifactId>hadoop-hdfs</artifactId>
        <version>2.7.7</version>
    </dependency>
</dependencies>
  1. 在項目的 src/main/resources 目錄下,新建一個文件,命名爲 log4j.properties,在文件中填入
log4j.rootLogger=INFO, stdout
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d %p [%c] - %m%n
log4j.appender.logfile=org.apache.log4j.FileAppender
log4j.appender.logfile.File=target/spring.log
log4j.appender.logfile.layout=org.apache.log4j.PatternLayout
log4j.appender.logfile.layout.ConversionPattern=%d %p [%c] - %m%n
  1. 創建包 hdfs 並創建 HdfsClient 類
package hdfs;

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;

import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;

public class HDFSClient {

    public static void main(String[] args) throws IOException, URISyntaxException, InterruptedException {

        // 1.獲取hdfs客戶端對象
        Configuration conf = new Configuration();
//        conf.set("fs.defaultFS", "hdfs://master:9000");
//        FileSystem fs = FileSystem.get(conf);
        FileSystem fs = FileSystem.get(new URI("hdfs://master:9000"), conf, "root");

        // 2.在hdfs上創建路徑
        fs.mkdirs(new Path("/usr/lyh/tmp"));

        // 3.關閉資源
        fs.close();
    }
}
  1. 執行程序

    說明:上面代碼中如果使用註釋的代碼,運行時需要配置用戶名稱
    在這裏插入圖片描述
    客戶端去操作 HDFS 時,是有一個用戶身份的。默認情況下,HDFS 客戶端 API 會從 JVM 中獲取一個參數來作爲自己的用戶身份:-DHADOOP_USER_NAME=root,root 爲用戶名稱。

3.2 HDFS 的 API 操作

3.2.1 HDFS 文件上傳、下載、刪除、更名

  1. 將 hdfs-site.xml 拷貝到項目的根目錄下
<configuration>
	<property>
		<name>dfs.replication</name>
        <value>1</value>
	</property>
</configuration>
  1. 編寫源代碼
@Test
public void testHDFSAPI() throws IOException, InterruptedException, URISyntaxException {
    // 獲取文件系統
    Configuration configuration = new Configuration();
    configuration.set("dfs.replication", "2");
    FileSystem fs = FileSystem.get(new URI("hdfs://master:9000"), configuration, "root");

    // 1 文件上傳
    fs.copyFromLocalFile(new Path("f:/0/input/input.txt"), new Path("/input.txt"));

    // 2 文件下載
    // boolean delSrc 指是否將原文件刪除
    // Path src 指要下載的文件路徑
    // Path dst 指將文件下載到的路徑
    // boolean useRawLocalFileSystem 是否開啓文件校驗
    fs.copyToLocalFile(false, new Path("/input.txt"), new Path("f:/0/output/input.txt"), true);

    // 3 文件夾刪除
    fs.delete(new Path("/tmp/"), true);

    // 4 文件名更改
    fs.rename(new Path("/input.txt"), new Path("/input1.txt"));

    // 關閉資源
    fs.close();
}
  1. 參數優先級

    參數優先級排序:(1)客戶端代碼中設置的值 >(2)ClassPath下的用戶自定義配置文件 >(3)然後是服務器的默認配置

3.2.2 HDFS 文件詳情查看

/**
* 文件詳情查看
* 查看文件名稱、權限、長度、塊信息
*/
@Test
public void testListFiles() throws IOException, InterruptedException, URISyntaxException {

	// 1獲取文件系統
	Configuration configuration = new Configuration();
	FileSystem fs = FileSystem.get(new URI("hdfs://master:9000"), configuration, "root");

	// 2 獲取文件詳情
	RemoteIterator<LocatedFileStatus> listFiles = fs.listFiles(new Path("/"), true);
	
	while (listFiles.hasNext()) {
   		LocatedFileStatus status = listFiles.next();

    	// 輸出詳情
    	// 文件名稱
   		System.out.println(status.getPath().getName());
    	// 長度
    	System.out.println(status.getLen());
    	// 權限
    	System.out.println(status.getPermission());
    	// 分組
    	System.out.println(status.getGroup());

    	// 獲取存儲的塊信息
    	BlockLocation[] blockLocations = status.getBlockLocations();

    	for (BlockLocation blockLocation : blockLocations) {

        	// 獲取塊存儲的主機節點
        	String[] hosts = blockLocation.getHosts();

        	for (String host : hosts) {
            	System.out.println(host);
        	}
    	}
    	System.out.println("---------------------");
	}

	// 3 關閉資源
    fs.close(); 
}       

3.2.3 HDFS 文件和文件夾判斷

/**
* 文件和文件夾判斷
 */
@Test
public void testListStatus() throws IOException, InterruptedException, URISyntaxException {

    // 1 獲取文件配置信息
    Configuration configuration = new Configuration();
    FileSystem fs = FileSystem.get(new URI("hdfs://master:9000"), configuration, "root");

    // 2 判斷是文件還是文件夾
    FileStatus[] listStatus = fs.listStatus(new Path("/"));

    for (FileStatus fileStatus : listStatus) {

        // 如果是文件
        if (fileStatus.isFile()) {
            System.out.println("f:" + fileStatus.getPath().getName());
        } else {
            System.out.println("d:" + fileStatus.getPath().getName());
        }
    }

    // 3 關閉資源
    fs.close();
}

3.3 HDFS的 I/O 流操作

  上面的 API 操作 HDFS 系統都是框架封裝好的。我們可以採用 IO 流的方式實現數據的上傳和下載來實現上述 API 的操作。

3.3.1 HDFS 文件上傳

@Test
public void putFileToHDFS() throws IOException, InterruptedException, URISyntaxException {

    // 1 獲取文件系統
    Configuration configuration = new Configuration();
    FileSystem fs = FileSystem.get(new URI("hdfs://master:9000"), configuration, "root");

    // 2 創建輸入流
    FileInputStream fis = new FileInputStream(new File("f:/0/input/input.txt"));

    // 3 獲取輸出流
    FSDataOutputStream fos = fs.create(new Path("/input.txt"));

    // 4 流對拷
    IOUtils.copyBytes(fis, fos, configuration);

    // 5 關閉資源
    IOUtils.closeStream(fos);
    IOUtils.closeStream(fis);
    fs.close();
}

3.3.2 HDFS 文件下載

@Test
public void getFileFromHDFS() throws IOException, InterruptedException, URISyntaxException{

    // 1 獲取文件系統
    Configuration configuration = new Configuration();
    FileSystem fs = FileSystem.get(new URI("hdfs://master:9000"), configuration, "root");

    // 2 獲取輸入流
    FSDataInputStream fis = fs.open(new Path("/input.txt"));

    // 3 獲取輸出流
    FileOutputStream fos = new FileOutputStream(new File("f:/0/output/output.txt"));

    // 4 流的對拷
    IOUtils.copyBytes(fis, fos, configuration);

    // 5 關閉資源
    IOUtils.closeStream(fos);
    IOUtils.closeStream(fis);
    fs.close();
}

3.3.3 定位文件讀取

  1. 下載第一塊
@Test
public void readFileSeek1() throws IOException, InterruptedException, URISyntaxException{

	// 1 獲取文件系統
	Configuration configuration = new Configuration();
	FileSystem fs = FileSystem.get(new URI("hdfs://hadoop102:9000"), configuration, "atguigu");
		
	// 2 獲取輸入流
	FSDataInputStream fis = fs.open(new Path("/hadoop-2.7.7.tar.gz"));
		
	// 3 創建輸出流
	FileOutputStream fos = new FileOutputStream(new File("f:/hadoop-2.7.2.tar.gz.part1"));
		
	// 4 流的拷貝
	byte[] buf = new byte[1024];
		
	for(int i =0 ; i < 1024 * 128; i++){
		fis.read(buf);
		fos.write(buf);
	}
		
	// 5關閉資源
	IOUtils.closeStream(fis);
	IOUtils.closeStream(fos);
	fs.close();
}

  1. 下載第二塊
@Test
public void readFileSeek2() throws IOException, InterruptedException, URISyntaxException{

	// 1 獲取文件系統
	Configuration configuration = new Configuration();
	FileSystem fs = FileSystem.get(new URI("hdfs://master:9000"), configuration, "root");
		
	// 2 打開輸入流
	FSDataInputStream fis = fs.open(new Path("/hadoop-2.7.7.tar.gz"));
		
	// 3 定位輸入數據位置
	fis.seek(1024*1024*128);
		
	// 4 創建輸出流
	FileOutputStream fos = new FileOutputStream(new File("f:/hadoop-2.7.7.tar.gz.part2"));
		
	// 5 流的對拷
	IOUtils.copyBytes(fis, fos, configuration);
		
	// 6 關閉資源
	IOUtils.closeStream(fis);
	IOUtils.closeStream(fos);
}
  1. 合併文件

    在 Window 命令窗口中進入到目錄 F:\,然後執行如下命令,對數據進行合併

type hadoop-2.7.7.tar.gz.part2 >> hadoop-2.7.7.tar.gz.part1

  合併完成後,將 hadoop-2.7.7.tar.gz.part1 重新命名爲 hadoop-2.7.7.tar.gz。解壓發現該 tar 包非常完整。

4. HDFS 的數據流

4.1 HDFS 寫數據流程

4.1.1 寫數據流程

在這裏插入圖片描述

  1. 客戶端通過 Distributed FileSystem 模塊向 NameNode 請求上傳文件,NameNode 檢查目標文件和父目錄是否存在。
  2. NameNode 返回是否可以上傳。
  3. 客戶端請求第一個 Block上傳到哪幾個 DataNode 服務器上。
  4. NameNode 返回 3 個DataNode節點,分別爲 dn1、dn2、dn3。
  5. 客戶端通過 FSDataOutputStream 模塊請求 dn1 上傳數據,dn1 收到請求會繼續調用 dn2,然後 dn2調用 dn3,將這個通信管道建立完成。
  6. dn1、dn2、dn3 逐級應答客戶端。
  7. 客戶端開始往 dn 1上傳第一個 Block(先從磁盤讀取數據放到一個本地內存緩存),以 Packet 爲單位,dn1 收到一個 Packet 就會傳給 dn2,dn2 傳給 dn3;dn1 每傳一個 packet 會放入一個應答隊列等待應答。
  8. 當一個 Block 傳輸完成之後,客戶端再次請求 NameNode上傳第二個 Block 的服務器。(重複執行 3-7 步)。

4.1.2 網絡拓撲-節點距離計算

在這裏插入圖片描述

4.1.3 機架感知(副本存儲節點選擇)

在這裏插入圖片描述
  對於常見情況,當複製因子爲 3 時,HDFS 的放置策略是將一個副本放在本地機架中的一個節點上,將另一個副本放在本地機架中的不同節點上,將最後一個副本放在不同機架中的另一個節點上。

  此策略減少了機架間寫入流量,這通常提高了寫入性能。機架故障的可能性遠低於節點故障的可能性;此策略不會影響數據可靠性和可用性保證。

4.2 HDFS 讀數據流程

在這裏插入圖片描述

  1. 客戶端通過 Distributed FileSystem 向 NameNode 請求下載文件,NameNode 通過查詢元數據,找到文件塊所在的 DataNode 地址。
  2. 挑選一臺 DataNode(就近原則,然後隨機)服務器,請求讀取數據。
  3. DataNode 開始傳輸數據給客戶端(從磁盤裏面讀取數據輸入流,以 Packet 爲單位來做校驗)。
  4. 客戶端以 Packet 爲單位接收,先在本地緩存,然後寫入目標文件。

5. NameNode 和 SecondaryNameNode

5.1 NN 和 2NN 工作機制

  NameNode 中的元數據存儲在內存中。
  如果 NameNode 中的元數據存儲在 NameNode 節點的磁盤中,因爲經常需要進行隨機訪問,還有響應客戶請求,必然是效率過低。因此,元數據需要存放在內存中。但如果只存在內存中,一旦斷電,元數據丟失,整個集羣就無法工作了。因此產生在磁盤中備份元數據的 FsImage(鏡像文件)。
  這樣又會帶來新的問題,當在內存中的元數據更新時,如果同時更新 FsImage,就會導致效率過低,但如果不更新,就會發生一致性問題,一旦 NameNode 節點斷電,就會產生數據丟失。因此,引入 Edits 文件(編輯日誌,只進行追加操作,效率很高)。每當元數據有更新或者添加元數據時,修改內存中的元數據並追加到 Edits 中。這樣,一旦 NameNode 節點斷電,可以通過 FsImage 和 Edits 的合併,合成元數據。
  但是,如果長時間添加數據到 Edits 中,會導致該文件數據過大,效率降低,而且一旦斷電,恢復元數據需要的時間過長。因此,需要定期進行 FsImage 和 Edits 的合併,如果這個操作由 NameNode 節點完成,又會效率過低。因此,引入一個新的節點 SecondaryNamenode,專門用於FsImage 和 Edits 的合併。

在這裏插入圖片描述

  1. 第一階段:NameNode啓動

    (1)第一次啓動 NameNode 格式化後,創建 Fsimage 和 Edits 文件。若不是第一次啓動,直接加載 Fsimage 和 Edits 到內存。
    (2)客戶端對元數據進行增刪改的請求。
    (3)NameNode 記錄操作日誌,更新滾動日誌。
    (4)NameNode 在內存中對數據進行增刪改。

  2. 第二階段:Secondary NameNode工作

    (1)Secondary NameNode 詢問 NameNode 是否需要 CheckPoint。直接帶回 NameNode 是否檢查結果。
    (2)Secondary NameNode 請求執行 CheckPoint。
    (3)NameNode 滾動正在寫的Edits日誌。
    (4)將滾動前的編輯日誌和鏡像文件拷貝到 Secondary NameNode。
    (5)Secondary NameNode 加載編輯日誌和鏡像文件到內存,併合並。
    (6)生成新的鏡像文件 fsimage.chkpoint。
    (7)拷貝 fsimage.chkpoint 到 NameNode。
    (8)NameNode 將 fsimage.chkpoint 重新命名成 fsimage。

5.2 Fsimage 和 Edits 解析

  1. 概念

    NameNode 被格式化之後,將在 /hadoop/hadoop-2.7.7/data/tmp/dfs/name/current 目錄中產生如下文件

    Fsimage 文件(鏡像文件):

      HDFS 文件系統元數據的一個永久性的檢查點,其中包含 HDFS 文件系統的所有目錄和文件 inode 的序列化信息。

    Edits 文件(編輯日誌):

      存放 HDFS 文件系統的所有更新操作的路徑,文件系統客戶端執行的所有寫操作首先會被記錄到 edits 文件中。

    seen_txid

      文件保存的是一個數字,就是最後一個 edits_的數字

  2. oiv 查看 Fsimage 文件

    ① 基本語法

hdfs oiv -p 文件類型 -i 鏡像文件 -o 轉換後文件輸出路徑

  ② 實操

hdfs oiv -p XML -i fsimage_0000000000000000262 -o /hadoop/hadoop-2.7.7/fsimage.xml
  1. oev 查看 Edits 文件

    ① 基本語法

hdfs oev -p 文件類型 -i 編輯日誌 -o 轉換後文件輸出路徑

  ② 實操

hdfs oev -p XML -i edits_0000000000000000261-0000000000000000262 -o /hadoop/hadoop-2.7.7/edits.xml

5.3 CheckPoint 時間設置

  1. 通常情況下,SecondaryNameNode 每隔一小時執行一次

    hdfs-default.xml

<property>
  <name>dfs.namenode.checkpoint.period</name>
  <value>3600</value>
</property>
  1. 一分鐘檢查一次操作次數,當操作次數達到 1 百萬時,SecondaryNameNode 執行一次。
<property>
  <name>dfs.namenode.checkpoint.txns</name>
  <value>1000000</value>
<description>操作動作次數</description>
</property>

<property>
  <name>dfs.namenode.checkpoint.check.period</name>
  <value>60</value>
<description> 1分鐘檢查一次操作次數</description>
</property >

5.4 NameNode 故障處理

方法一:將 SecondaryNameNode 中數據拷貝到 NameNode 存儲數據的目錄

  1. kill -9 NameNode 進程
  2. 刪除 NameNode 存儲的數據(/hadoop/hadoop-2.7.7/data/tmp/dfs/name)
rm -rf /hadoop/hadoop-2.7.7/data/tmp/dfs/name/*
  1. 拷貝 SecondaryNameNode 中數據到原 NameNode 存儲數據目錄
scp -r slave2:/hadoop/hadoop-2.7.7/data/tmp/dfs/namesecondary/* ./name/
  1. 重新啓動 NameNode
hadoop-daemon.sh start namenode

方法二:使用 -importCheckpoint 選項啓動 NameNode 守護進程,從而將 SecondaryNameNode 中數據拷貝到 NameNode 目錄中。

  1. 修改 hdfs-site.xml
<property>
  <name>dfs.namenode.checkpoint.period</name>
  <value>120</value>
</property>

<property>
  <name>dfs.namenode.name.dir</name>
  <value>/hadoop/hadoop-2.7.7/data/tmp/dfs/name</value>
</property>
  1. kill -9 NameNode 進程

  2. 刪除 NameNode 存儲的數據(/hadoop/hadoop-2.7.7/data/tmp/dfs/name)

rm -rf /hadoop/hadoop-2.7.7/data/tmp/dfs/name/*
  1. 如果 SecondaryNameNode 不和 NameNode 在一個主機節點上,需要將 SecondaryNameNode 存儲數據的目錄拷貝到 NameNode 存儲數據的平級目錄(/hadoop/hadoop-2.7.7/data/tmp/dfs),並刪除 in_use.lock (namesecondary 目錄下)文件
scp -r slave2:/hadoop/hadoop-2.7.7/data/tmp/dfs/namesecondary ./
rm -rf in_use.lock
  1. 導入檢查點數據(等待一會ctrl+c結束掉)
hdfs namenode -importCheckpoint
  1. 啓動 NameNode
hadoop-daemon.sh start namenode

5.5 集羣安全模式

  1. 概述

  ① NameNode 啓動時,首先將映像文件(fsimage)載入內存,並執行編輯日誌(edits)中的各項操作。一旦在內存中成功建立文件系統元數據的映像,則創建一個新的鏡像文件和一個空的編輯日誌。此時,NameNode 開始監聽 DataNode 請求。但是此刻,NameNode 運行在安全模式,即 NameNode 的文件系統對於客戶端來說是隻讀的。

  ② 系統中的數據塊的位置並不是由 NameNode 維護的,而是以塊列表的形式存儲在 DataNode 中。在系統的正常操作期間,NameNode 會在內存中保留所有塊位置的映射信息。在安全模式下,各個 DataNode 會向 NameNode 發送最新的塊列表信息,NameNode 瞭解到足夠多的塊位置信息之後,即可高效運行文件系統。

  ③ 如果滿足“最小複本條件”,NameNode 會在 30 秒鐘之後就退出安全模式。所謂的最小複本條件指的是在整個文件系統中 99.9% 的塊滿足最小複本級別(默認值:dfs.replication.min=1)。在啓動一個剛剛格式化的 HDFS 集羣時,因爲系統中還沒有任何塊,所以 NameNode 不會進入安全模式。

  1. 基本語法

    集羣處於安全模式,不能執行重要操作(寫操作)。集羣啓動完成後,自動退出安全模式。

    ① 查看安全模式狀態

hdfs dfsadmin -safemode get

  ② 進入安全模式狀態

hdfs dfsadmin -safemode enter

  ③ 離開安全模式狀態

hdfs dfsadmin -safemode leave

  ④ 等待安全模式狀態

hdfs dfsadmin -safemode wait

5.6 NameNode 多目錄配置

  NameNode 的本地目錄可以配置成多個,且每個目錄存放內容相同,增加了可靠性

  1. 停止集羣,刪除 data 和 logs 中所有數據。
  2. 在 hdfs-site.xml 文件中增加如下內容
<property>
    <name>dfs.namenode.name.dir</name>
	<value>file:///${hadoop.tmp.dir}/dfs/name1,file:///${hadoop.tmp.dir}/dfs/name2</value>
</property>
  1. 格式化集羣並啓動
hdfs namenode -format
start-dfs.sh

6. DataNode

6.1 DataNode 工作機制

在這裏插入圖片描述

  1. 一個數據塊在 DataNode 上以文件形式存儲在磁盤上,包括兩個文件,一個是數據本身,一個是元數據包括數據塊的長度,塊數據的校驗和,以及時間戳。
  2. DataNode 啓動後向 NameNode 註冊,通過後,週期性(1小時)的向 NameNode 上報所有的塊信息。
  3. 心跳是每 3 秒一次,心跳返回結果帶有 NameNode 給該 DataNode 的命令如複製塊數據到另一臺機器,或刪除某個數據塊。如果超過 10 分鐘沒有收到某個 DataNode 的心跳,則認爲該節點不可用。
  4. 集羣運行中可以安全加入和退出一些機器。

6.2 數據完整性

  如下是 DataNode 節點保證數據完整性的方法

  1. 當 DataNode 讀取 Block 的時候,它會計算 CheckSum。
  2. 如果計算後的 CheckSum,與 Block 創建時值不一樣,說明 Block 已經損壞。
  3. Client 讀取其他 DataNode 上的 Block。
  4. DataNode 在其文件創建後周期驗證 CheckSum,如下圖所示。

在這裏插入圖片描述

6.3 掉線時限參數設置

在這裏插入圖片描述
  需要注意的是 hdfs-site.xml 配置文件中的 heartbeat.recheck.interval 的單位爲毫秒,dfs.heartbeat.interval 的單位爲秒。

<property>
    <name>dfs.namenode.heartbeat.recheck-interval</name>
    <value>300000</value>
</property>
<property>
    <name>dfs.heartbeat.interval</name>
    <value>3</value>
</property>

6.4 服役新數據節點

  1. 環境準備

    ① 在 slave2 主機上再克隆一臺 slave3 主機
    ② 修改 IP 地址和主機名稱
    ③ 刪除原來 HDFS 文件系統留存的文件(/hadoop/hadoop-2.7.7/data 和 log)

  2. 服役新節點具體步驟

    ① 修改集羣所有節點(master 和所有 slave)的 ${HADOOP_HOME}/conf/slaves 文件,增加新節點 ip 或 hostname:

master
slave1
slave2
slave3

  ② 集羣所有節點上修改系統的/ etc/hosts 配置文件,增加 ip 與 hostname 的映射

192.168.217.133 slave3

  ③ 啓動DataNode,即可關聯到集羣

hadoop-daemon.sh start datanode
yarn-daemon.sh start nodemanager

在這裏插入圖片描述

6.5 退役舊數據節點

6.5.1 添加白名單

  添加到白名單的主機節點,都允許訪問 NameNode,不在白名單的主機節點,都會被退出。

  1. 在 NameNode 的 /hadoop/hadoop-2.7.7/etc/hadoop 目錄下創建 dfs.hosts 文件

    添加如下主機名稱(不添加 slave3)

master
slave1
slave2
  1. 在 NameNode 的 hdfs-site.xml 配置文件中增加 dfs.hosts 屬性
<property>
	<name>dfs.hosts</name>
	<value>/hadoop/hadoop-2.7.7/etc/hadoop/dfs.hosts</value>
</property>
  1. 配置文件分發
xsync hdfs-site.xml
  1. 刷新 NameNode、刷新 ResourceManager
hdfs dfsadmin -refreshNodes
yarn rmadmin -refreshNodes
  1. 在 web 瀏覽器上查看

6.5.2 黑名單退役

  在黑名單上面的主機都會被強制退出。

  1. 在 NameNode 的 /hadoop/hadoop-2.7.7/etc/hadoop 目錄下創建 dfs.hosts.exclude 文件

    添加如下主機名稱(要退役的節點)

slave3
  1. 在 NameNode 的 hdfs-site.xml 配置文件中增加 dfs.hosts.exclude 屬性
<property>
	<name>dfs.hosts.exclude</name>
 	<value>/hadoop/hadoop-2.7.7/etc/hadoop/dfs.hosts.exclude</value>
</property>
  1. 刷新 NameNode、刷新 ResourceManager
hdfs dfsadmin -refreshNodes
yarn rmadmin -refreshNodes
  1. 檢查 Web 瀏覽器,退役節點的狀態爲 decommission in progress(退役中),說明數據節點正在複製塊到其他節點
  2. 等待退役節點狀態爲 decommissioned(所有塊已經複製完成),停止該節點及節點資源管理器。注意:如果副本數是 3,服役的節點小於等於 3,是不能退役成功的,需要修改副本數後才能退役

6.6 Datanode 多目錄配置

  DataNode 也可以配置成多個目錄,每個目錄存儲的數據不一樣。即:數據不是副本。

  1. 停止集羣,刪除 data 和 logs 中所有數據。
  2. 在 hdfs-site.xml 文件中增加如下內容
<property>
 	<name>dfs.datanode.data.dir</name>
	<value>file:///${hadoop.tmp.dir}/dfs/data1,file:///${hadoop.tmp.dir}/dfs/data2</value>
</property>
  1. 格式化集羣並啓動
hdfs namenode -format
start-dfs.sh

7. HDFS 2.X 新特性

7.1 集羣間數據拷貝

  採用 distcp 命令實現兩個 Hadoop 集羣之間的遞歸數據複製

hadoop distcp hdfs://hadoop1:9000/hello.txt hdfs://hadoop2:9000/hello.txt

7.2 小文件存檔

  1. hdfs 存儲小文件弊端

      每個文件均按塊存儲,每個塊的元數據存儲在 namenode 的內存中,因此 hadoop 存儲小文件會非常低效。因爲大量的小文件會耗盡 namenode 中的大部分內存。但注意,存儲小文件所需要的磁盤容量和存儲這些文件原始內容所需要的磁盤空間相比也不會增多。例如,一個 1 MB 的文件以大小爲 128 MB 的塊存儲,使用的是 1 MB 的磁盤空間,而不是 128 MB。

  2. 解決存儲小文件辦法之一

      Hadoop 存檔文件或 HAR 文件,是一個更高效的文件存檔工具,它將文件存入 HDFS 塊,在減少 namenode 內存使用的同時,允許對文件進行透明的訪問。具體說來,Hadoop 存檔文件對內還是一個一個獨立文件,對 namenode 而言卻是一個整體,減少了 namenode 的內存。
    在這裏插入圖片描述

  3. 案例實操

    ① 需要啓動 yarn 進程

 start-yarn.sh

  ② 歸檔文件

   把 /usr/input/ 目錄裏面的所有文件歸檔成一個叫 input.har 的歸檔文件,並把歸檔後文件存儲到 /usr/input/ 路徑下。

hadoop archive -archiveName input.har -p /usr/input  /usr/output

  ③ 查看歸檔

hadoop fs -ls -R har:///usr/output/input.har

在這裏插入圖片描述

  ④ 解歸檔

hadoop fs -cp har:///usr/output/input.har/* /user/output

7.3 回收站

  開啓回收站功能,可以將刪除的文件在不超時的情況下,恢復原數據,起到防止誤刪除、備份等作用。

  1. 回收站參數設置及工作機制

    ① 默認值 fs.trash.interval=0, 0 表示禁用回收站;其他值表示設置文件的存活時間。
    ② 默認值 fs.trash.checkpoint.interval=0,檢查回收站的間隔時間。
    ③ 要求 fs.trash.checkpoint.interval <= fs.trash.interval。

    在這裏插入圖片描述

  2. 啓用回收站

    ① 修改 core-site.xml,配置垃圾回收時間爲 10 分鐘
    ② 進入垃圾回收站用戶名稱,默認是 dr.who,修改爲 root 用戶

<!-- 回收站文件的存活時間 -->
<property>
  <name>fs.trash.interval</name>
  <value>10</value>
</property>

<!-- 修改垃圾回收站用戶名稱 -->
<property>
  <name>hadoop.http.staticuser.user</name>
  <value>root</value>
</property>
  1. 示例:刪除 /input.txt
hadoop fs -rm /input.txt

在這裏插入圖片描述

  1. 查看回收站

    回收站在集羣中的路徑:/user/root/.Trash/….
    在這裏插入圖片描述

  2. 恢復回收站數據

hadoop fs -mv /user/root/.Trash/Current/input.txt /
  1. 清空回收站
hadoop fs -expunge
  1. 通過程序刪除的文件不會經過回收站,需要調用 moveToTrash() 才進入回收站
Trash trash = New Trash(conf);
trash.moveToTrash(path);

7.4 快照管理

  快照相當於對目錄做一個備份。並不會立即複製所有文件,而是記錄文件變化。

命令 說明
hdfs dfsadmin -allowSnapshot 路徑 開啓指定目錄的快照功能
hdfs dfsadmin -disallowSnapshot 路徑 禁用指定目錄的快照功能,默認禁用
hdfs dfs -createSnapshot 路徑 對目錄創建快照
hdfs dfs -createSnapshot 路徑 名稱 指定名稱創建快照
hdfs dfs -renameSnapshot 路徑 舊名稱 新名稱 重命名快照
hdfs lsSnapshottableDir 列出當前用戶所有可快照目錄
hdfs snapshotDiff 路徑1 路徑2 比較兩個快照目錄的不同之處
hdfs dfs -deleteSnapshot 路徑 名稱 刪除快照
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章