1. 程式人生 > >大資料入門學習筆記(叄)- 布式檔案系統HDFS

大資料入門學習筆記(叄)- 布式檔案系統HDFS

文章目錄

HDFS概述及設計目標

如果讓我們自己來設計一個分散式檔案系統,咋辦?
下圖是普通分散式檔案系統
在這裡插入圖片描述

什麼是HDFS

  • Hadoop實現了一個分散式檔案系統( Hadoop Distributed File System) ,簡稱HDFS
  • 源自Google的GFS論文
  • 發表於2003年,HDFS是GFS的克隆版

HDFS的設計目標

  • 非常巨大的分散式檔案系統
  • 執行在普通廉價的硬體上
  • 易擴充套件、為使用者提供效能不錯的檔案儲存服務

HDFS架構

HDFS有主從架構。HDFS叢集由一個NameNode組成,它是一個主伺服器,管理檔案系統名稱空間並管理客戶機對檔案的訪問。此外,還有許多datanode,通常每個節點一個,管理連線到它們執行的節點的儲存。HDFS公開一個檔案系統名稱空間,並允許使用者資料儲存在檔案中。在內部,檔案被分割成一個或多個塊,這些塊儲存在一組資料節點(datanode)中。NameNode執行檔案系統操作,如開啟、關閉和重新命名檔案和目錄。它還確定塊到datanode的對映。datanode負責服務來自檔案系統客戶端的讀寫請求。根據NameNode的指令,datanode還執行塊的建立、刪除和複製。
在這裡插入圖片描述


上圖中

  • 1 個Master(NameNode/NN) 帶 N個Slaves(DataNode/DN)
    HDFS/YARN/HBase其實都是一樣的
  • 1個檔案會被拆分成多個Block理解為:
    blocksize:128M
    130M ==> 2個Block: 128M 和 2M

NN:
1)負責客戶端請求的響應
2)負責元資料(檔案的名稱、副本系數、Block存放的DN)的管理

DN:
1)儲存使用者的檔案對應的資料塊(Block)
2)要定期向NN傳送心跳資訊,彙報本身及其所有的block資訊,健康狀況

一個典型的部署是一臺機器執行一個namenade,叢集中的其他機器都執行一個DataNode。該體系結構不排除在同一臺機器上執行多個DataNode
但在實際部署中卻很少出現這種情況。

HDFS副本機制

HDFS支援傳統的分層檔案組織。使用者或應用程式可以在這些目錄中建立目錄並存儲檔案。檔案系統名稱空間層次結構與大多數現有檔案系統相似;可以建立和刪除檔案,將檔案從一個目錄移動到另一個目錄,或者重新命名檔案。
NameNode維護檔案系統名稱空間。對檔案系統名稱空間或其屬性的任何更改都由NameNode記錄。應用程式可以指定由HDFS維護的檔案的副本數量。一個檔案的副本數量稱為該檔案的副本因子。這些資訊由NameNode儲存。
在這裡插入圖片描述
資料副本
HDFS被設計為在大型叢集中的機器之間可靠地儲存非常大的檔案。它以塊序列的形式儲存每個檔案。為了容錯,複製檔案的塊。每個檔案的塊大小和複製因子都是可配置的。

除了最後一個塊之外,檔案中的所有塊大小都相同。

應用程式可以指定檔案的副本數量。副本因子可以在檔案建立時指定,以後可以更改。HDFS中的檔案只寫一次(除了追加和截斷之外),並且在任何時候都有一個寫入者

NameNode就塊的複製做出所有決定。它定期從叢集中的每個datanode接收心跳和資料塊報告。接收到心跳錶示DataNode正常工作。塊報表包含datanode上所有塊的列表。

副本存放策略在這裡插入圖片描述

上圖代表資料中心,兩個機架,黃色代表客戶端所在的節點(預設三個副本)

第一個副本存放在同client的節點上面;
第二個副本存放在不同第一個副本機架的隨意一個節點;
第三個副本存放在與第二個副本相同機架的另一個節點上;
如果只有一個機架,則在不同節點儲存;如果高於三個副本則高於三的隨意挑選機架和節點。

HDFS環境搭建

官網安裝文件
Hadoop偽分散式安裝步驟
http://hadoop.apache.org/docs/stable/hadoop-project-dist/hadoop-common/SingleCluster.html

  1. jdk安裝
    解壓:tar -zxvf jdk-7u79-linux-x64.tar.gz -C ~/app
    新增到系統環境變數: ~/.bash_profile
    export JAVA_HOME=/home/hadoop/app/jdk1.7.0_79
    export PATH= J A V A H O M E / b i n : JAVA_HOME/bin: PATH
    使得環境變數生效: source ~/.bash_profile
    驗證java是否配置成功: java -v

  2. 安裝ssh
    sudo yum install ssh
    ssh-keygen -t rsa
    cp ~/.ssh/id_rsa.pub ~/.ssh/authorized_keys

  3. 下載並解壓hadoop
    下載:直接去cdh網站下載
    解壓:tar -zxvf hadoop-2.6.0-cdh5.7.0.tar.gz -C ~/app

  4. hadoop配置檔案的修改(hadoop_home/etc/hadoop)
    hadoop-env.sh

export JAVA_HOME=/home/hadoop/app/jdk1.7.0_79

core-site.xml

<property>
    <name>fs.defaultFS</name>
    <value>hdfs://hadoop000:8020</value>
</property>

<property>
    <name>hadoop.tmp.dir</name>
    <value>/home/hadoop/app/tmp</value>
</property>

hdfs-site.xml

<property>
    <name>dfs.replication</name>
    <value>1</value>
</property>
  1. 啟動hdfs
    格式化檔案系統(僅第一次執行即可,不要重複執行):hdfs/hadoop namenode -format
    啟動hdfs: sbin/start-dfs.sh
    驗證是否啟動成功:
    jps
    DataNode
    SecondaryNameNode
    NameNode

     瀏覽器訪問方式: http://hadoop000:50070
    
  2. 停止hdfs
    sbin/stop-dfs.sh

HDFS shell

HDFS shell常用命令的使用

官網文件參考
http://hadoop.apache.org/docs/stable/hadoop-project-dist/hadoop-hdfs/HDFSCommands.html#dfs
hdfs dfs等於hadoop fs

Is

[root@hadoop data]# hdfs dfs -ls /
18/11/09 21:40:49 WARN util.NativeCodeLoader: Unable to load native-hadoop library for your platform... using builtin-java classes where applicable
Found 1 items
drwxr-xr-x   - root supergroup          0 2018-09-02 08:37 /hbase

get

[root@hadoop data]# hdfs dfs -get /hell.txt

mkdir

[root@hadoop data]# hdfs dfs -mkdir -p /text/a/b
18/11/09 21:44:57 WARN util.NativeCodeLoader: Unable to load native-hadoop library for your platform... using builtin-java classes where applicable
[root@hadoop data]# hdfs dfs -ls -R /text
18/11/09 21:46:25 WARN util.NativeCodeLoader: Unable to load native-hadoop library for your platform... using builtin-java classes where applicable
drwxr-xr-x   - root supergroup          0 2018-11-09 21:44 /text/a
drwxr-xr-x   - root supergroup          0 2018-11-09 21:44 /text/a/b

rm

[root@hadoop data]# hdfs dfs -rm -R  /text/a/b

put

[root@hadoop data]# hdfs dfs -put hell.txt /
18/11/09 21:43:16 WARN util.NativeCodeLoader: Unable to load native-hadoop library for your platform... using builtin-java classes where applicable
[root@hadoop data]# hdfs dfs -ls /
18/11/09 21:43:25 WARN util.NativeCodeLoader: Unable to load native-hadoop library for your platform... using builtin-java classes where applicable
Found 2 items
drwxr-xr-x   - root supergroup          0 2018-09-02 08:37 /hbase
-rw-r--r--   1 root supergroup         13 2018-11-09 21:43 /hell.txt

Java API操作

package com.imooc.hadoop.hdfs;

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.*;
import org.apache.hadoop.io.IOUtils;
import org.apache.hadoop.util.Progressable;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.net.URI;

/**
 * Hadoop HDFS Java API 操作
 */
public class HDFSApp {

    public static final String HDFS_PATH = "hdfs://hadoop000:8020";

    FileSystem fileSystem = null;
    Configuration configuration = null;


    /**
     * 建立HDFS目錄
     */
    @Test
    public void mkdir() throws Exception {
        fileSystem.mkdirs(new Path("/hdfsapi/test"));
    }

    /**
     * 建立檔案
     */
    @Test
    public void create() throws Exception {
        FSDataOutputStream output = fileSystem.create(new Path("/hdfsapi/test/a.txt"));
        output.write("hello hadoop".getBytes());
        output.flush();
        output.close();
    }

    /**
     * 檢視HDFS檔案的內容
     */
    @Test
    public void cat() throws Exception {
        FSDataInputStream in = fileSystem.open(new Path("/hdfsapi/test/a.txt"));
        IOUtils.copyBytes(in, System.out, 1024);
        in.close();
    }


    /**
     * 重新命名
     */
    @Test
    public void rename() throws Exception {
        Path oldPath = new Path("/hdfsapi/test/a.txt");
        Path newPath = new Path("/hdfsapi/test/b.txt");
        fileSystem.rename(oldPath, newPath);
    }

    /**
     * 上傳檔案到HDFS
     *
     * @throws Exception
     */
    @Test
    public void copyFromLocalFile() throws Exception {
        Path localPath = new Path("/Users/rocky/data/hello.txt");
        Path hdfsPath = new Path("/hdfsapi/test");
        fileSystem.copyFromLocalFile(localPath, hdfsPath);
    }

    /**
     * 上傳檔案到HDFS
     */
    @Test
    public void copyFromLocalFileWithProgress() throws Exception {
        InputStream in = new BufferedInputStream(
                new FileInputStream(
                        new File("/Users/rocky/source/spark-1.6.1/spark-1.6.1-bin-2.6.0-cdh5.5.0.tgz")));

        FSDataOutputStream output = fileSystem.create(new Path("/hdfsapi/test/spark-1.6.1.tgz"),
                new Progressable() {
                    public void progress() {
                        System.out.print(".");  //帶進度提醒資訊
                    }
                });


        IOUtils.copyBytes(in, output, 4096);
    }


    /**
     * 下載HDFS檔案
     */
    @Test
    public void copyToLocalFile() throws Exception {
        Path localPath = new Path("/Users/rocky/tmp/h.txt");
        Path hdfsPath = new Path("/hdfsapi/test/hello.txt");
        fileSystem.copyToLocalFile(hdfsPath, localPath);
    }

    /**
     * 檢視某個目錄下的所有檔案
     */
    @Test
    public void listFiles() throws Exception {
        FileStatus[] fileStatuses = fileSystem.listStatus(new Path("/"));

        for(FileStatus fileStatus : fileStatuses) {
            String isDir = fileStatus.isDirectory() ? "資料夾" : "檔案";
            short replication = fileStatus.getReplication();
            long len = fileStatus.getLen();
            String path = fileStatus.getPath().toString();

            System.out.println(isDir + "\t" + replication + "\t" + len + "\t" + path);
        }

    }

    /**
     * 刪除
     */
    @Test
    public void delete() throws Exception{
        fileSystem.delete(new Path("/"), true);
    }


    @Before
    public void setUp() throws Exception {
        System.out.println("HDFSApp.setUp");
        configuration = new Configuration();
        fileSystem = FileSystem.get(new URI(HDFS_PATH), configuration, "hadoop");
    }

    @After
    public void tearDown() throws Exception {
        configuration = null;
        fileSystem = null;

        System.out.println("HDFSApp.tearDown");
    }

}

HDFS檔案讀寫流程

在這裡插入圖片描述
首先看看出場的角色,第一個是client客戶端,用來發起讀寫請求,讀取HDFS上的檔案或往HDFS中寫檔案;第二個是Namenode,唯一的一個,會協調所有客戶端發起的請求;第三個是DataNode,負責資料儲存,跟Namenode不一樣,DataNode有很多個,有時候能達到數以千計。

檔案寫流程圖解

在這裡插入圖片描述
在這裡插入圖片描述
往HDFS中寫資料的流程如下:

  • 第1幅圖:我們跟客戶端說,你幫我寫一個200M的資料吧,客戶端說沒問題啊,但是…
  • 第2幅圖:客戶端不知道我們對資料有沒有其他的要求啊,問我們是不是忘了什麼東西呢?我們想起來我們還是有要求的,第一我們要把資料分成若干塊,並且每塊的大小是128M,第二,每個資料塊應該複製3份。其實這就是我們說的HDFS的檔案分塊和多副本,如果你不說的話客戶端怎麼知道到底怎麼分,複製多少份呢?
  • 第3幅圖:由上面的對話我們發現,如果對於每個檔案客戶端都要這麼問一下,是不是太麻煩了?所以說一個好的客戶端應該是,使用者就算不說你也要知道有這兩個屬性:塊的大小,一個檔案應該按照怎樣的大小切分(通常是64M或128M);複製因子,每個塊應該複製多少份(通常是3份),也就是說如果使用者不主動提供這些屬性,那麼就按照預設的來。
  • 第4幅圖:現在客戶端已經知道了每個塊的大小了,那麼把200M的檔案分成128M和72M兩個塊,一個長一個短。
  • 第5幅圖:切分後客戶端就開始工作了,既然有兩個塊,那先上傳第一個塊,於是客戶端請求Namenode幫它寫一個128M的塊,並且要複製3份。
  • 第6幅圖:Namenode接受到客戶端的請求後,既然需要3個副本,那麼就需要找到3個DataNode,Namenode就會想怎麼去找到這3個DataNode呢?我該告訴客戶端哪些資訊呢?於是它就去它管理的DataNode中找一些滿足要求的空閒節點。
  • 第7幅圖:Namenode找到了3個節點,現在把找到的節點發給客戶端,表示:兄弟,你不是要我幫你寫資料嘛,我給你找到了這3個合適的DataNode,並且已經按距離遠近給你排過序了,第一個是最近的,你把資料給他們讓他們幫你寫吧。
  • 第8幅圖:客戶端收到3個DataNode地址後,直接把資料傳送到第一個節點(DataNode1)上,然後DataNode1開始把資料寫到他的硬碟中。
  • 第9、10、11幅圖:DataNode1在接受資料的同時,會把剛剛收到的資料傳送到第二個DataNode2上,同理DataNode2也是,接收的同時把資料立馬發給DataNode3,到了DataNode3已經是最後一個DataNode了。整個過程跟流水線一樣,接收一點就發一點。(個人感覺跟計算機網路中令牌環網的工作原理有些類似)
    第12幅圖:Namenode是所有DataNode的老大,所以DataNode在存完資料後要跟老大彙報,告訴他說,我第一個塊的資料已經寫完了。
  • 第13幅圖:3個DataNode都報告完成後,好,這樣第一個資料塊就寫完了,下面對第二個塊重複這個步驟。
  • 第14幅圖:所有的塊都寫完了之後,客戶端關閉跟Namenode的連線。這時Namenode已經儲存了檔案的元資料,也就是檔案被拆成了幾塊,複製了幾份,每塊分別儲存在哪個DataNode上。
  • 最後一幅圖說明了每個角色在寫資料過程中的作用:
    Client:切分檔案成資料塊。
    Namenode:對於每個資料塊,找到儲存的DataNode地址。
    DataNode:多副本方式儲存資料。

檔案讀流程圖解

在這裡插入圖片描述

  • 第1幅圖:寫檔案已經搞定了,那麼怎麼讀檔案呢?我們先跟客戶端說,嘿兄弟!幫我讀個檔案唄!
  • 第2幅圖:客戶端跟Namenode發了個請求,把檔名傳送給Namenode,表示我想要這個這個檔案的資訊。
  • 第3幅圖:Namenode找了找,然後找到了一個結果,結果包含這個檔案被拆成了多少塊,每個塊儲存在哪些DataNode上的資訊,並且DataNode同樣是按照距離排序的。然後把這個結果傳送給客戶端,說,嘿兄弟!你要的檔案在這些DataNode上,你去找吧。
  • 第4幅圖:現在客戶端知道了檔案的儲存情況,所以就一個個去DataNode上訪問就好了。
  • 最後提出了一個問題:如果這個過程中DataNode掛了,或者資料在傳輸中出了問題怎麼辦?事實上HDFS對於這些問題都是能夠完美解決的。

HDFS錯誤處理機制

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

HDFS優缺點

優點:

  • 資料冗餘、硬體容錯
  • 處理流式的資料訪問
  • 適合儲存大檔案
  • 可構建在廉價機器上

缺點:

  • 低延遲的資料訪問
  • 不適合小檔案儲存