我們在上一篇《向Hadoop Say Hello——初識Hadoop》中已經對HDFS進行了簡單介紹,還不清楚HDFS是什麼的朋友可以點擊上面鏈接先看上一篇,在這一篇中我們聚焦HDFS,從HDFS的設計架構和重要概念開始學習,然後會學習HDFS的命令行操作以及Java Api操作。
特別提示:本文所進行的演示都是在hadoop-2.6.0-cdh5.15.1的版本進行的
一、HDFS概述
HDFS 是 Hadoop Distribute File System,Hadoop分佈式文件系統的簡稱。這個文件系統是一個適用於大的數據集的支持高吞吐和高容錯的運行在通用(廉價)機上的分佈式文件系統。
設計目標
- 故障的快速檢測和自動恢復(容錯)
分佈式的文件系統將由成百上千臺的機器共同組成集羣,在集羣中每一臺機器都可能出現故障,當出現部分機器出現故障時,保證服務的正常提供是一個重要的工作,因此故障的快遞檢測和自動快速恢復是 HDFS 的核心設計目標
- 流式數據訪問(高吞吐)
HDFS 的設計是爲了存儲大文件,它更注重數據訪問的吞吐量,對訪問的延時性要求較低,它不適用於低延時的場景,爲了保證大吞吐量的數據訪問,HDFS 被設計成了更適合批量處理,且以流式讀取數據的方式
- 存儲大的數據集
通常情況下在HDFS上運行的應用都具有大的數據集,一個典型的 HDFS 文件大小是 GB 到 TB 的級別,在實際中超大型集羣的HDFS中文件的大小也可能達到PB級別
- 簡化數據一致性模型
大部分運行在 HDFS 上的應用對文件都是"一次寫入,多次讀出"訪問模型。所以HDFS就作出了一個文件一旦創建、寫入成功後就不需要再修改了的假設,這一假設簡化了數據一致性問題,使高吞吐量的數據訪問成爲可能(後文會分析HDFS的數據一致性問題)
- 移動計算的位置比移動數據的位置更划算
由於網絡帶寬是一個有限資源,一個計算任務的運行,當計算數據離計算邏輯所在節點越近這個計算任務執行的就越高效,數據量越大這種特點就越明顯。所以在 HDFS 中設計了提供給應用將計算邏輯移動到數據附近這樣的接口
- 可移植性
爲了方便HDFS作爲大規模數據應用平臺得到廣泛應用,HDFS被設計成了一個易於從一個平臺移植到另一個平臺的數據應用系統
二、HDFS中一些概念
Block(塊)
HDFS和一般的磁盤文件系統一樣,也有Block的概念,和一般文件相同的是,用戶存儲的文件都會被按照Block Size被拆分存儲,但與一般磁盤文件系統不同的是,當一個文件的實際大小比Block Size小的時候,它不會佔用Block Size大小的物理空間。這句話如何理解呢?
咱們舉個栗子,HDFS的默認塊大小是128M,假設現在有一個200M的文件那麼存儲在HDFS上時就會被拆分成兩個Block存儲,分別是一個128M的塊和一個72M的塊,且他們佔用的物理空間也是200M。而在一般的磁盤文件系統中塊的大小是4K,不同點就是如果一個文件大小是5K那麼它存儲在磁盤文件系統中所佔用的物理空間大小是8k,在一般磁盤文件系統中文件大小沒有超過一個塊大小時,是按照一個塊的大小佔用空間。
文件系統命名空間
HDFS和傳統的文件系統一樣支持分層的文件組織結構。支持常用的文件創建,刪除、移動,重命名等操作,並且也支持用戶配額和訪問權限控制。現有的HDFS實現不支持硬鏈接或軟鏈接。
副本機制
爲了容錯,每個文件被拆分後的 Block 都是以副本的方式存儲,每個文件的塊大小和以及複製的副本系數都是可以配置,HDFS 默認是3副本配置。
在集羣中副本的放置位置對HDFS的可靠性和性能都有很大的影響,所以優化副本擺放策略在一個大型集羣中顯得尤爲重要。在HDFS中是使用一種機架感知的副本放置策略,這種策略是基於提高數據可靠性、可用性和網絡帶寬利用率而設計的一種策略
在機架感知副本策略下,當副本系數是3時,HDFS的文件副本放置方式如下:如果寫入程序位於數據節點上,則將一個副本放在本地計算機上,否則放在與寫入程序位於同一機架中的隨機數據節點上,另一個副本放在不同(遠程)機架中的節點上,最後一個放在同一遠程機架中的不同節點上。
二、HDFS的架構
HDFS 是一個主從架構的服務。一個 HDFS 集羣包括一個 NameNode 節點、一個 SecondaryNameNode 節點(非必須)和多個 DataNode 節點。
NameNode
NameNode是一個主節點,是所有 HDFS 元數據的決策器和存儲庫,它管理文件系統名稱空間、確定塊到數據節點的映射,並且控制客戶端對文件的訪問。但是 NameNode 從不與用戶數據直接交互,與用戶數據交互的工作都由DataNode 來做。
DataNode
DataNode是管理它們運行的節點的數據存儲,通常一個集羣中每個節點一個都有一個 DataNode。DataNode 負責爲來自客戶端的讀寫請求提供服務,同時它還會根據 NameNode 的指令執行塊創建、刪除和複製。
SecondaryNameNode
SecondaryNameNode是NameNode的一個冷備份,可以看做是一個CheckPoint,它主要負責合併 fsimage 和 fsedits,並推送給 NameNode。它也會維護一個合併後的 Namespace Image 副本, 可用於在 NameNode 故障後恢復數據
架構圖
接下來咱們可以看下面這張圖(圖片來自Hadoop官網),這就是HDFS的一個架構圖
圖中的幾個要點:
-
NameNode管理着Metadata(元數據)
-
客戶端client對元數據的操作是指向NameNode,對用戶數據的讀寫是通過DataNode
-
NameNode向DataNode發送Block的操作命令
-
一塊的副本被存儲在不同的機架中
HDFS Federation
通過了解架構大家是否發現了一個問題?在 HDFS 的主從架構中 NameNode 成了集羣擴大的瓶頸,隨着集羣增大和文件數據量增多,單個 NameNode 性能就無法滿足需求,這也就意味着,HDFS 集羣的大小是受 NameNode 節點約束的。
HDFS Federation( HDFS 聯邦)就是爲了解決這種問題,它提供了一種橫向擴展 NameNode 的方式。在 Federation 模式中,每個 NameNode 管理命名空間的一部分,同時也各自維護一個 Block Pool,保存 Block 的節點映射信息。各 NameNode 節點之間是獨立的,一個節點的失敗不會導致其他節點管理的文件不可用,同時失敗的節點所管理的文件數據也就不可訪問,所以 HDFS Federation 只是解決 HDFS 的拓展問題,並不是解決單點故障問題的。
四、HDFS的安裝和使用
在剛開始學習的過程中建議先使用單機版,避免因爲集羣的問題提高了學習門檻,打擊自己學習信心,在熟練其使用和原理後在使用集羣也不遲。
HDFS的安裝可以參照《教你安裝單機版Hadoop》,這篇文章裏面有講述HFDS的安裝。
HDFS安裝成功後如下操作
-
第一次啓動前需要對文件系統進行格式化:
${HADOOP_HOME}/bin/hdfs name -format
-
啓動HDFS:
${HADOOP_HOME}/sbin/start-dfs.sh
在HDFS啓動後,我們就可以訪問 HDFS 的 Web 頁面,查看HDFS的信息,HDFS web 訪問默認端口是50070,瀏覽器打開效果如下:
首頁是一個Overview概覽信息,裏面包括了集羣基本信息(Hadoop版本、集羣ID、Block Pool ID等)、存儲摘要以及NameNode的運行狀態等。
在頂部第二個主菜單下是DataNode的信息,頁面效果如下:
在主菜單的最後一項Utilities下面有一個Browse the file system,這個頁面就是HDFS的一個文件瀏覽器,可以查看在HDFS上存儲的文件,效果如下:
四、HDFS基本操作(命令和Java Api)
HDFS 包含的常用操作如下
命令操作
HDFS的命令有兩種使用方式
-
通過hadoop命令:
hadoop fs [options]
,eg:./hadoop fs -ls / -
通過hdfs命令:
hdfs dfs [options]
,eg:./hdfs dfs -ls /
其實兩種方法調用的是相同的腳本代碼。
常用命令中有一部分是類似於Linux的文件管理命令,對於這一部分命令可以類比Linux中的使用,我們就不講解了,我們只把HDFS中一些特別的命令學習一下。
類似Linux系統的文件管理命令有:ls, cat, chgrp, chmod, chown, cp, df, du, find, mkdir, mv, rm, rmdir, tail, test
我們學習一下這些命令:
-
appendToFile:將一個或者多個文件添加到HDFS系統中
-
copyFromLocal:將本地文件拷貝到HDFS
-
count:統計hdfs對應路徑下的目錄個數,文件個數,文件總計大小
-
expunge:從垃圾箱目錄永久刪除超過保留閾值的檢查點中的文件,並創建新檢查點
-
get:從HDFS上傳輸文件到本地
-
getmerge:把hdfs指定目錄下的所有文件內容合併到本地linux的文件中
-
moveFromLocal:把本地文件移動到HDFS上,本地文件會被刪除
-
moveToLocal:把HDFS文件移動到本地,HDFS文件會被刪除
-
put:將文件上傳到HDFS上
-
setrep:修改文件或目錄副本數
-
stat:輸出指定目錄的信息,可設置格式化輸出(%b: 文件大小,%n: 文件名,%o: 塊大小,%r: 副本個數,%y, %Y: 修改日期)
-
text:格式化輸出文件的內容,允許的格式化包括zip,和 TextRecordInputStream
-
touchz:在指定目錄創建一個新文件,如果文件存在,則創建失敗
Hadoop的文件系統
在使用Java的Api前我們現先了解一下Hadoop的抽象文件系統
org.apache.hadoop.fs.FileSystem 是Hadoop中抽象的一個文件系統,我們今天所使用的HDFS只是這個抽象的文件系統的實現之一,HDFS提供了幾個主要的實現包括:LocalFileSystem、 DistributeFileSystem、 WebHdfsFileSystem、 HarFileSystem、 ViewFileSystem、 FtpFileSystem、 S3AFileSystem
-
LocalFileSystem:對本地文件系統的一個實現
-
DistributeFileSystem:HDFS的實現
-
WebHdfsFileSystem:通過Http請求提供對HDFS的認證和讀寫操作的實現,還有一個SWebHdfsFileSystem是基於Https的實現
-
HarFileSystem:這是Hadoop存檔文件系統的實現,歸檔後的多個文件可以進行壓縮成一個文件
-
ViewFileSystem:視圖文件系統,只是邏輯上唯一的一個視圖文件系統,主要是來做HDFS Federation的時候,將不同集羣中的文件系統進行映射,在邏輯上形成一個文件系統,在客戶端屏蔽底層細節
-
FtpFileSystem:基於ftp協議的一個文件系統實現
-
S3AFileSystem: 基於S3A協議的一個文件系統實現,S3是Amazon的對象存儲服務
Java Api
- pom中加入hadoop-client依賴
<repositories>
<!--使用的Hadoop是CDH版本的需要引用一下cloudera的倉庫庫-->
<repository>
<id>cloudera</id>
<url>https://repository.cloudera.com/artifactory/cloudera-repos/</url>
</repository>
</repositories>
<dependencies>
<!--Hadoop客戶端-->
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-client</artifactId>
<version>2.6.0-cdh5.15.1</version>
</dependency>
</dependencies>
- 一個簡單的演示代碼
package site.teamo.learning.hdfs;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FSDataInputStream;
import org.apache.hadoop.fs.FSDataOutputStream;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import java.io.Closeable;
import java.io.IOException;
public class HdfsApp {
/**
* @param args
* @throws IOException
*/
public static void main(String[] args) throws IOException {
FileSystem fs = null;
try {
//創建配置,設置Hdfs的地址信息
Configuration configuration = new Configuration();
configuration.set("fs.defaultFS", "hdfs://hdh100:8020");
//在系統環境變量設置使用的用戶名
System.setProperty("HADOOP_USER_NAME", "root");
//打開一個文件系統
fs = FileSystem.get(configuration);
//創建一個目錄
fs.mkdirs(new Path("/dream-hammer"));
//判斷是否是目錄
fs.isDirectory(new Path("/dream-hammer"));
//創建一個文件dream1.txt,返回值boolean類型,true:創建成功;false:創建失敗
fs.createNewFile(new Path("/dream-hammer/dream1.txt"));
//判斷文件是否存在,返回值是boolean類型,true:存在;false:不存在
fs.exists(new Path("/dream-hammer/dream1.txt"));
//向文件追加內容
FSDataOutputStream out = null;
try {
out = fs.append(new Path("/dream-hammer/dream1.txt"));
out.writeUTF("dream-hammer");
out.flush();
out.close();
} finally {
close(out);
}
//打開dream1的輸入流
FSDataInputStream in = null;
try {
in = fs.open(new Path("/dream-hammer/dream1.txt"));
System.out.println(in.readUTF());
in.close();
} finally {
close(in);
}
//刪除目錄和文件
fs.delete(new Path("/dream-hammer"), true);
} finally {
close(fs);
}
}
/**
* 關閉流,釋放資源
* @param closeable
*/
public static void close(Closeable closeable) {
if (closeable != null) {
try {
closeable.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
** 總結 ** :這篇文章主要是對HDFS的基礎理論知識進行了介紹和說明,同時也介紹了HDFS的基本使用,比較基礎易懂,也適合剛接觸HDFS的朋友閱讀學習,錘子會在下一篇文章裏面介紹 HDFS讀寫數據流程,這個屬於更深層次的東西了,關注【愛做夢的錘子】,一起學習進步
文章歡迎轉載,轉載請註明出處,個人公衆號【愛做夢的錘子】,全網同id,個站 http://te-amo.site,歡迎關注,裏面會分享更多有用知識,還有我的私密照片