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);
}
}