1. 程式人生 > >HDFS基本原理及資料存取實戰

HDFS基本原理及資料存取實戰

---------------------------------------------------------------------------------------------------------------

[版權申明:本文系作者原創,轉載請註明出處]

作者:朱培

---------------------------------------------------------------------------------------------------------------


本文主要介紹了hdfs的基本原理、hdfs基本操作、hdfs的讀取資料流程、namenode工作機制,rpc程式設計以及常見的兩種必會的面試題等,旨在於全面深入的理解HDFS的基本工作流程並可以開發出簡易的HDFS的程式。

一、HDFS簡介

Hadoop分散式檔案系統(HDFS)被設計成適合執行在通用硬體(commodity hardware)上的分散式檔案系統。HDFS體系結構中有兩類節點,一類是NameNode,又叫"元資料節點";另一類是DataNode,又叫"資料節點"。這兩類節點分別承擔Master和Worker具體任務的執行節點。總的設計思想:分而治之——將大檔案、大批量檔案,分散式存放在大量獨立的伺服器上,以便於採取分而治之的方式對海量資料進行運算分析。

HDFS是一個主/從(Mater/Slave)體系結構,從終端使用者的角度來看,它就像傳統的檔案系統一樣,可以通過目錄路徑對檔案執行CRUD(Create、Read、Update和Delete)操作。但由於分散式儲存的性質,HDFS叢集擁有一個NameNode和一些DataNode。NameNode管理檔案系統的元資料,DataNode儲存實際的資料。客戶端通過同NameNode和DataNodes的互動訪問檔案系統。客戶端聯絡NameNode以獲取檔案的元資料,而真正的檔案I/O操作是直接和DataNode進行互動的。


HDFS一般是用來“一次寫入,多次讀取”,不適合做實時互動性很強的事情,不適合儲存大量小檔案(當然,如果你偏要存大量小檔案的話本文末尾會有解決方案).

二、HDFS工作原理

2.1基本原理

1 分散式檔案系統,它所管理的檔案是被切塊儲存在若干臺datanode伺服器上.

2 hdfs提供了一個統一的目錄樹來定位hdfs中的檔案,客戶端訪問檔案時只要指定目錄樹的路徑即可,不用關心檔案的具體物理位置.

3 每一個檔案的每一個切塊,在hdfs叢集中都可以儲存多個備份(預設3份),在hdfs-site.xml中,dfs.replication的value的數量就是備份的數量.

4 hdfs中有一個關鍵程序服務程序:namenode,它維護了一個hdfs的目錄樹及hdfs目錄結構與檔案真實儲存位置的對映關係(元資料).而datanode服務程序專門負責接收和管理"檔案塊"-block.預設大小為128M(可配置),(dfs.blocksize).(老版本的hadoop的預設block是64M的)


2.2 常用操作

hdfs的根目錄,通過這條命令可以檢視hdfs儲存的檔案,在hadoop的安裝目錄下面:
bin/hdfs dfs -ls /<pre name="code" class="html">hdfs dfs -chmod 600 /test.txt    //設定檔案許可權


bin/hdfs dfs -df -h /    //檢視磁碟空間


bin/hdfs dfs -mkdir /aaa   //新建資料夾


bin/hdfs dfs  -tail       //檢視檔案尾部

hdfs塊的儲存位置位於:(在hadoop的根目錄下面的tmp資料夾中)hadoop-2.5.2/tmp/hadoop/dfs/data/current/BP-33587274-127.0.0.1-1465370300743/current/finalized

從圖中我們可以看到hdfs儲存的塊的,當然這裡面的兩個塊通過合併可以形成一個新的檔案,然後可以通過解壓進行驗證。

touch full hadoop.tar.gz
cat blk_1073741825 >> hadoop.tar.gz
cat blk_1073741826 >> hadoop.tar.gz
tar -xvzf  hadoop.tar.gz 

同樣,在hadoop的安裝目錄中,直接用命令來存取檔案可以通過下面的命令:

存檔案:
./hdfs dfs -put /home/admin1/桌面/test.txt  hdfs://localhost:9000/
取檔案:
./hdfs dfs -get hdfs://localhost:9000/test.txt


hdfs的許可權控制:

客戶端的操作命令

bin/hdfs dfs -chown  aa:bgroup  /test.txt    

//將test.txt檔案的許可權改為aa使用者的bgroup組.

(因為我根本就沒有這個組和使用者,但是也可以修改成功,所以說明hadoop本身對許可權的控制不是很嚴格)

2.3 HDFS的shell操作

--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

-chgrp

-chmod

-chown

上面三個跟linux中的用法一樣

-->hadoop fs -chmod 666 /hello.txt

-copyFromLocal    #從本地檔案系統中拷貝檔案到hdfs路徑去

-->hadoop fs  -copyFromLocal  ./jdk.tar.gz /aaa/

-copyToLocal      #從hdfs拷貝到本地

Eg: hadoop fs -copyToLocal /aaa/jdk.tar.gz

-count         #統計一個指定目錄下的檔案節點數量

-->hadoop fs -count /aaa/

-cp              #hdfs的一個路徑拷貝hdfs的另一個路徑

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

-createSnapshot

-deleteSnapshot

-renameSnapshot

以上三個用來操作hdfs檔案系統目錄資訊快照

-->hadoop fs -createSnapshot /

-df               #統計檔案系統的可用空間資訊

-du

-->hadoop fs -df -h /

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

-get              #等同於copyToLocal,就是從hdfs下載檔案到本地

-getmerge             #合併下載多個檔案

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

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

-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

-moveToLocal              #從hdfs剪下貼上到本地

-mv                     #hdfs目錄中移動檔案

-put                #等同於copyFromLocal

-rm                #刪除檔案或資料夾

--> hadoop fs -rm -r/aaa/bbb/

-rmdir                 #刪除空目錄

-setrep                #設定hdfs中檔案的副本數量

-->hadoop fs -setrep 3 /aaa/jdk.tar.gz

-stat                  #顯示一個檔案或資料夾的元資訊

-tail                  #顯示一個檔案的末尾

-text                  #以字元形式列印一個檔案的內容


三、HDFS寫入資料流程解析

3.1 基本原理

hdfs的資料寫入是非常複雜的過程,下面來看一下簡要的步驟。



那麼問題來了,如果他們之間的一個datanode突然壞掉了怎麼辦。

1、如果傳輸過程中,有某個datanode出現了故障,那麼當前的pipeline會被關閉,出現故障的datanode會從當前的pipeline中移除,剩餘的block會繼續剩下的datanode中繼續以pipeline的形式傳輸,同時Namenode會分配一個新的datanode,保持replicas設定的數量。
2、關閉pipeline,將ack queue中的資料塊放入data queue的開始。
3、當前的資料塊在已經寫入的資料節點中被元資料節點賦予新的標示,則錯誤節點重啟後能夠察覺其資料塊是過時的,會被刪除。
4、失敗的資料節點從pipeline中移除,另外的資料塊則寫入pipeline中的另外兩個資料節點。
5、元資料節點則被通知此資料塊是複製塊數不足,將來會再建立第三份備份。
6、客戶端呼叫create()來建立檔案
7、DistributedFileSystem用RPC呼叫元資料節點,在檔案系統的名稱空間中建立一個新的檔案。
8、元資料節點首先確定檔案原來不存在,並且客戶端有建立檔案的許可權,然後建立新檔案。
9、DistributedFileSystem返回DFSOutputStream,客戶端用於寫資料。
10、客戶端開始寫入資料,DFSOutputStream將資料分成塊,寫入data queue。
11、Data queue由Data Streamer讀取,並通知元資料節點分配資料節點,用來儲存資料塊(每塊預設複製3塊)。分配的資料節點放在一個pipeline裡。
12、Data Streamer將資料塊寫入pipeline中的第一個資料節點。第一個資料節點將資料塊傳送給第二個資料節點。第二個資料節點將資料傳送給第三個資料節點。
13、DFSOutputStream為發出去的資料塊儲存了ack queue,等待pipeline中的資料節點告知資料已經寫入成功。

3.2 編碼實踐

這段程式碼的意思是從/home/admin1/hadoop/eclipse.tar.gz上傳檔案到hdfs檔案中,URI中寫的是hdfs的地址
@Test
	public void testUpload() throws IOException, InterruptedException, URISyntaxException{
		Configuration conf = new Configuration();
	
		 
		//to get a client of the hdfs system
		FileSystem fs = FileSystem.get(new URI("hdfs://ubuntu2:9000"),conf,"admin1");
		

		fs.copyFromLocalFile(new Path("/home/admin1/hadoop/eclipse.tar.gz"), new Path("/"));
		fs.close();
		
		
	}


四、HDFS讀取資料流程解析

4.1 基本原理

讀取的流程比寫入的流程就簡單很多了。



4.2 編碼實踐

下載檔案:
/*public static void main(String[] args) throws IOException {
		//download  from hdfs
		
		Configuration conf = new Configuration();
		conf.set("fs.defaultFS", "hdfs://ubuntu2:9000/");
		
		
		conf.set("dfs.blocksize", "64");
		 
		//to get a client of the hdfs system
		FileSystem fs = FileSystem.get(conf);
		
		fs.copyToLocalFile(new Path("hdfs://ubuntu2:9000/test.txt"), new Path("/home/admin1/Downloads/test1.txt"));
		
		fs.close();
	}*/
刪除檔案:
@Test
	public void rmove() throws IOException, InterruptedException, URISyntaxException{
	Configuration conf = new Configuration();

	FileSystem fs = FileSystem.get(new URI("hdfs://ubuntu2:9000"),conf,"admin1");
	
	fs.delete(new Path("/test.txt"));
	
	fs.close();
	}


五、NameNode工作機制

元資料放在記憶體中,同時需要備份,通過日誌的記錄(對元資料有修改操作時)寫到磁碟中,用edits_ingropress,定期dump一次,當一旦斷電時,很難恢復,
所以我們就用sercondName定期把操作日誌下載和記憶體映象檔案,然後就定期合併操作記錄,(日誌+fsimage)然後生成一個新的namenode映象檔案  (fs.image.ckpt)
最後上傳給namenode,然後namenode重新命名為fsimage.


元資料管理機制:
1、元資料有3中儲存形式:記憶體、edits日誌、fsimage
2、最完整最新的元資料一定是記憶體中的這一部分

六、RPC程式設計

使用 RPC 程式設計是在客戶機和伺服器實體之間進行可靠通訊的最強大、最高效的方法之一。它為在分散式計算環境中執行的幾乎所有應用程式提供基礎。任何 RPC 客戶機-伺服器程式的重要實體都包括 IDL 檔案(介面定義檔案)、客戶機 stub、伺服器 stub 以及由客戶機和伺服器程式共用的標頭檔案。客戶機和伺服器 stub 使用 RPC 執行時庫通訊。RPC 執行時庫提供一套標準的執行時例程來支援 RPC 應用程式。在一般的應用程式中,被呼叫的過程在相同的地址空間中執行,並把結果返回給發出呼叫的過程。在分散式環境中,客戶機和伺服器在不同的機器上執行,客戶端呼叫在伺服器端執行的過程,並把結果傳送回客戶機。這稱為遠端過程呼叫 (RPC),是 RPC 程式設計的基礎。

以下例項是用來模擬使用者登陸的過程。

在linux中的eclipse。新建一個java工程,新建java檔案:

RPCServer.java

package hdfsTest;

public interface  RPCService {
	public static final long versionID=100L;  //定義通訊介面的版本號

	public String userLogin(String username,String password);  //使用者名稱和密碼

}
RPCServerImpl.java
package hdfsTest;

public class RPCServiceImpl  implements RPCService {

	@Override
	public String userLogin(String username, String password) {	
		return username+"  logged in successfully!";
	}
}

RPCControl.java
package hdfsTest;

import java.io.IOException;
import java.net.InetSocketAddress;

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.ipc.RPC;


//
public class RPCController {

	public static void main(String[] args) throws IOException {
		RPCService serverceImpl=RPC.getProxy(RPCService.class,100,new InetSocketAddress("ubuntu2",10000),new Configuration());
<span style="white-space:pre">		</span>//100是指前面設定的版本號,InetSocketAddress中的是hdfs主機名和10000是通訊埠
		String result=serverceImpl.userLogin("aaa", "aaa");  //設定使用者使用者名稱和密碼
		System.out.println(result);   
	}
}
ServerRunner.java
package hdfsTest;

import java.io.IOException;

import org.apache.hadoop.HadoopIllegalArgumentException;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.ipc.RPC;
import org.apache.hadoop.ipc.RPC.Builder;
import org.apache.hadoop.ipc.Server;

public class ServerRunner {

	public static void main(String[] args) throws HadoopIllegalArgumentException, IOException {
		Builder builder=new RPC.Builder(new Configuration());
		
		builder.setBindAddress("ubuntu2");  //hdfs的主機名
		builder.setPort(10000);    //設定通訊埠號
		
		builder.setProtocol(RPCService.class);
		builder.setInstance(new RPCServiceImpl());
		
		Server server=builder.build();
		server.start();   //開啟服務
	}
}

執行結果:


七、常見問題分析

7.1.HDFS可不可以用來做網盤

答案:不可以, 網盤只儲存,不用做分析,hdfs在增加節點,同時也加大了分析能力,主要是大容量儲存之上的資料分析
1、容量成本太高,2、檔案大小不確定,如果存大量小檔案會造成很大的浪費 3、相對於網盤來說,檔案讀寫的效率低  4、只適合一次寫入,多次讀取的操作
5、hdfs不支援檔案內容修改,可支援往檔案尾部追加內容。

7.2 HDFS大量小檔案儲存

1、對於待上傳的檔案,先將小檔案合併為一個大檔案再上傳,利用SequenceFile、MapFile、Har等方式歸檔小檔案,這個方法的原理就是把小檔案歸檔起來管理,HBase就是基於此的。對於這種方法,如果想找回原來的小檔案內容,那就必須得知道與歸檔檔案的對映關係。 2、如果對於已經上傳了的檔案,需要進行合併的話,我們可以使用Map-redure來進行歸檔。 3、BlueSky解決方案,以一種two-level prefetching機制以提高小檔案讀取效率,即索引檔案預取和資料檔案預取。索引檔案預取是指當用戶訪問某個檔案時,該檔案所在的block對應的索引檔案被載入到記憶體中,這樣,使用者訪問這些檔案時不必再與namenode互動了。資料檔案預取是指使用者訪問某個檔案時,將該檔案所在課件中的所有檔案載入到記憶體中,這樣,如果使用者繼續訪問其他檔案,速度會明顯提高。