產(chǎn)生背景
隨著數(shù)據(jù)量越來越大奠蹬,在一個操作系統(tǒng)存不下所有的數(shù)據(jù)掺冠,那么就分配到更多的操作系統(tǒng)管理的磁盤中菇曲,但是不方便管理和維護(hù)冠绢,迫切需要一種系統(tǒng)來管理多臺機(jī)器上的文件,這就是分布式文件管理系統(tǒng)常潮,HDFS只是分布式文件管理系統(tǒng)中的一種弟胀。
HDFS(Hadoop Distributed File System),它是一個文件系統(tǒng)喊式,用于存儲文件邮利,通過目錄樹來定位文件; 其次垃帅,它是分布式的延届,由很多服務(wù)器聯(lián)合起來來實現(xiàn)其功能,集群中的服務(wù)器有各自的角色贸诚。
HDFS的使用場景方庭,適合一次寫入,多次讀出的場景酱固,且不支持對文件的修改械念,適合用來做數(shù)據(jù)分析。
HDFS優(yōu)缺點(diǎn)
優(yōu)點(diǎn)
高容錯性
-
數(shù)據(jù)自動保存多個副本运悲,它通過增加副本的形式龄减,提高容錯性。
-
某一個副本丟失以后班眯,它可以自動恢復(fù)
適合處理大數(shù)據(jù)
- 數(shù)據(jù)規(guī)模: 能夠處理數(shù)據(jù)規(guī)模達(dá)到GB, TB,甚至PB級別的數(shù)據(jù)
- 文件規(guī)模: 能夠處理百萬規(guī)模以上的文件數(shù)量希停。
可構(gòu)建在廉價機(jī)器上烁巫,通過多副本機(jī)制,提高可靠性
缺點(diǎn)
- 不適合低延時的數(shù)據(jù)訪問宠能,比如毫秒級別的存儲數(shù)據(jù)亚隙,是做不到的
- 無法高效的對大量小文件進(jìn)行存儲。
- 存儲大量小文件的話违崇,它會占用NameNode大量的內(nèi)存來存儲文件目錄和塊信息阿弃,這樣是不可取的,因為namenode的內(nèi)存總是有限的羞延。
- 小文件存儲的尋址時間會超過讀取時間渣淳,它違反了HDFS的設(shè)計目標(biāo)。
- 不支持并發(fā)寫入伴箩,文件隨機(jī)修改入愧。
- 一個文件只能一個寫,不允許多個線程同時寫赛蔫;
-
僅支持?jǐn)?shù)據(jù)追加(append),不支持對文件的隨機(jī)修改砂客。
HDFS組成架構(gòu)
NameNode: 就是Master泥张, 它是一個主管呵恢, 管理者
- 管理HDFS的namespace
- 配置副本策略
- 管理數(shù)據(jù)塊(Block)映射信息
- 處理客戶端讀寫請求
DataNode
就是slave,NameNode下達(dá)命令媚创,DataNode執(zhí)行實際的操作渗钉。
- 存儲實際的塊
- 執(zhí)行實際數(shù)據(jù)塊的讀/寫操作
Client
就是客戶端
- 文件切分。文件上傳HDFS的時候钞钙,Client將文件切分成一個一個的Block,然后進(jìn)行上傳鳄橘。
- 與NameNode交互,獲取文件的位置信息
- 與DataNode交互芒炼,讀取或者寫入數(shù)據(jù)
- Client提供一些命令來管理HDFS瘫怜,比如NameNode格式化
- Client可以通過一些命令來訪問HDFS,比如對HDFS增刪改查操作本刽。
Secondary NameNode
并非NameNode的熱備鲸湃,當(dāng)NameNode掛掉的時候,它并不能馬上替換NameNode并提供服務(wù)子寓。
- 輔助NameNode,分擔(dān)其工作量暗挑,比如定期合并Fsimage和Edits, 并推送給NameNode
- 在緊急情況下,可輔助恢復(fù)NameNode(做HA)
HDFS 文件塊大小
HDFS中的文件在物理上都是分塊存儲(Block)斜友,塊的大小可以通過配置參數(shù)(dfs.blocksize)來規(guī)定炸裆,Hadoop2.x,默認(rèn)大小是128M鲜屏,老版本是64m.
為什么塊的大小不能設(shè)置太小或者太大烹看?
- HDFS塊設(shè)置的太小国拇,會增加尋址時間,(程序一直在找塊的開始位置)
- 如果塊設(shè)置的太大听系,從磁盤傳輸數(shù)據(jù)的時間會明顯大于定位這個塊開始位置所需的時間贝奇,導(dǎo)致程序在處理這塊數(shù)據(jù)時,會非常慢
HDFS塊的大小設(shè)置和磁盤傳輸速率有很大關(guān)系靠胜。
HDFS操作
package com.zouxxyy.hdfs;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.*;
import org.apache.hadoop.io.IOUtils;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.URI;
public class HDFSClient {
private FileSystem fs;
@Before
public void before() throws IOException, InterruptedException {
// 獲取HDFS的抽象對象
fs = FileSystem.get(URI.create("hdfs://server-2:9000"), new Configuration(), "xxx");
}
@Test
public void put() throws IOException, InterruptedException {
Configuration configuration = new Configuration();
configuration.setInt("dfs.replication", 1);
fs = FileSystem.get(URI.create("hdfs://server-2:9000"), configuration, "xxx");
// 本地文件上傳到HDFS
fs.copyFromLocalFile(new Path("data/input/wordCount/1.txt"), new Path("/"));
}
@Test
public void get() throws IOException{
// HDFS文件下載到本地
fs.copyToLocalFile(new Path("/1.txt"), new Path("./"));
}
@Test
public void rename() throws IOException{
// HDFS重命名
fs.rename(new Path("/1.txt"), new Path("/2.txt"));
}
@Test
public void delete() throws IOException{
// HDFS刪除
boolean delete = fs.delete(new Path("/1.txt"), true);
if (delete) {
System.out.println("刪除成功");
}
else{
System.out.println("刪除失敗");
}
}
@Test
public void append() throws IOException{
// HDFS 文件追加測試
FSDataOutputStream append = fs.append(new Path("/2.txt"), 1024);
FileInputStream open = new FileInputStream("data/input/wordCount/1.txt");
IOUtils.copyBytes(open, append, 1024, true);
}
@Test
public void ls() throws IOException{
// fileStatuses包含文件和文件夾
FileStatus[] fileStatuses = fs.listStatus(new Path("/"));
for (FileStatus fileStatus : fileStatuses) {
if(fileStatus.isFile()) {
System.out.println("文件:");
System.out.println(fileStatus.getPath());
System.out.println(fileStatus.getOwner());
}
else {
System.out.println("文件夾:");
System.out.println(fileStatus.getModificationTime());
System.out.println(fileStatus.getPermission());
}
}
}
@Test
public void listFiles() throws IOException {
// 注意listFiles方法只能得到文件
RemoteIterator<LocatedFileStatus> files = fs.listFiles(new Path("/"), true);
while (files.hasNext()) {
LocatedFileStatus file = files.next();
System.out.println("===========================");
System.out.println(file.getPath());
System.out.println("塊信息:");
BlockLocation[] blockLocations = file.getBlockLocations();
for (BlockLocation blockLocation : blockLocations) {
String[] hosts = blockLocation.getHosts();
System.out.print("塊在: ");
for(String host : hosts) {
System.out.println(host + " ");
}
}
}
}
@After
public void after() throws IOException {
fs.close();
}
}
參數(shù)優(yōu)先級: 客戶端代碼中設(shè)置的值 > ClassPath下用戶自定義的配置文件 > 服務(wù)器的默認(rèn)配置
上面的API操作HDFS都是框架封裝好的掉瞳,如果我們想自己實現(xiàn)上述API呢?
HDFS的IO流操作
更底層一點(diǎn)的操作
package com.zouxxyy.hdfs;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
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 org.apache.hadoop.io.IOUtils;
import org.junit.Test;
public class HDFSIO {
// 把本地e盤上的banhua.txt文件上傳到HDFS根目錄
@Test
public void putFileToHDFS() throws IOException, InterruptedException, URISyntaxException{
// 1 獲取對象
Configuration conf = new Configuration();
FileSystem fs = FileSystem.get(new URI("hdfs://server-2:9000"), conf , "xxx");
// 2 獲取輸入流
FileInputStream fis = new FileInputStream(new File("data/input/wordCount/1.txt"));
// 3 獲取輸出流
FSDataOutputStream fos = fs.create(new Path("/test.txt"));
// 4 流的對拷
IOUtils.copyBytes(fis, fos, conf);
// 5 關(guān)閉資源
IOUtils.closeStream(fos);
IOUtils.closeStream(fis);
fs.close();
}
// 從HDFS上下載banhua.txt文件到本地e盤上
@Test
public void getFileFromHDFS() throws IOException, InterruptedException, URISyntaxException{
// 1 獲取對象
Configuration conf = new Configuration();
FileSystem fs = FileSystem.get(new URI("hdfs://server-2:9000"), conf , "xxx");
// 2 獲取輸入流
FSDataInputStream fis = fs.open(new Path("/banhua.txt"));
// 3 獲取輸出流
FileOutputStream fos = new FileOutputStream(new File("e:/banhua.txt"));
// 4 流的對拷
IOUtils.copyBytes(fis, fos, conf);
// 5 關(guān)閉資源
IOUtils.closeStream(fos);
IOUtils.closeStream(fis);
fs.close();
}
// 下載第一塊
@Test
public void readFileSeek1() throws IOException, InterruptedException, URISyntaxException{
// 1 獲取對象
Configuration conf = new Configuration();
FileSystem fs = FileSystem.get(new URI("hdfs://server-2:9000"), conf , "xxx");
// 2 獲取輸入流
FSDataInputStream fis = fs.open(new Path("/1.txt"));
// 3 獲取輸出流
FileOutputStream fos = new FileOutputStream(new File("./1.txt.part1"));
// 4 流的對拷(只拷貝128m)
byte[] buf = new byte[1024];
for (int i = 0; i < 1024 * 128; i++) {
fis.read(buf);
fos.write(buf);
}
// 5 關(guān)閉資源
IOUtils.closeStream(fos);
IOUtils.closeStream(fis);
fs.close();
}
// 下載第二塊
@SuppressWarnings("resource")
@Test
public void readFileSeek2() throws IOException, InterruptedException, URISyntaxException{
// 1 獲取對象
Configuration conf = new Configuration();
FileSystem fs = FileSystem.get(new URI("hdfs://server-2:9000"), conf , "xxx");
// 2 獲取輸入流
FSDataInputStream fis = fs.open(new Path("/hadoop-2.7.2.tar.gz"));
// 3 設(shè)置指定讀取的起點(diǎn)
fis.seek(1024*1024*128);
// 4 獲取輸出流
FileOutputStream fos = new FileOutputStream(new File("e:/hadoop-2.7.2.tar.gz.part2"));
// 5 流的對拷
IOUtils.copyBytes(fis, fos, conf);
// 6 關(guān)閉資源
IOUtils.closeStream(fos);
IOUtils.closeStream(fis);
fs.close();
}
}
HDFS的數(shù)據(jù)流
寫數(shù)據(jù)流程
- 客戶端通過Distributed FileSystem模塊向NameNode請求上傳文件浪漠,NameNode檢查目標(biāo)文件是否已存在陕习,父目錄是否存在。
- NameNode返回是否可以上傳
- 客戶端請求第一個Block上傳到哪幾個DataNode服務(wù)器上址愿。
- NameNode返回3個DataNode節(jié)點(diǎn)该镣,分別為dn1,dn2,dn3
- 客戶端通過FSDataOutputStream模塊請求dn1上傳數(shù)據(jù),dn1收到請求會繼續(xù)調(diào)用dn2响谓,然后dn2調(diào)用dn3损合,
- dn1、dn2娘纷、dn3逐級應(yīng)答客戶端嫁审。
- 客戶端開始往dn1上傳第一個block(先從磁盤讀取數(shù)據(jù)放到一個本地內(nèi)存緩存)以packet為單位, dn1收到第一個packet就會傳給dn2赖晶,dn2傳給dn3律适。
- 當(dāng)一個Block傳輸完成之后,客戶端再次請求NameNode遏插,請求下一個Block傳到哪幾個DataNode服務(wù)器上捂贿。(重復(fù)執(zhí)行3-7)
節(jié)點(diǎn)距離計算
在HDFS寫數(shù)據(jù)的過程中,NameNode會選擇距離帶上傳數(shù)據(jù)最近的DataNode接收數(shù)據(jù)胳嘲。這個最近距離怎么計算呢厂僧?
機(jī)架感知
For the common case, when the replication factor is three, HDFS’s placement policy is to put one replica on the local machine if the writer is on a datanode, otherwise on a random datanode, another replica on a node in a different (remote) rack, and the last on a different node in the same remote rack.
在默認(rèn)情況下,一個文件有三個副本了牛。當(dāng)writer(執(zhí)行寫請求的客戶端)在datanode上時颜屠,第一個副本寫在本機(jī)上;當(dāng)writer沒在datanode上時白魂,隨機(jī)選一個機(jī)架里的datanode放置汽纤。第二個副本放在和第一個副本不同的機(jī)架上的隨機(jī)daanode上。第三個副本和第二個副本在同一個機(jī)架福荸,但是在不同的datanode上蕴坪。
更多副本:隨機(jī)節(jié)點(diǎn)放置。
HDFS讀數(shù)據(jù)流程
- 客戶端通過DistributedFileSystem向NameNode請求下載文件,NameNode通過查詢元數(shù)據(jù)背传,找到文件塊所在的DataNode地址呆瞻。(按距離順序)
- 客戶端挑選一臺DataNode服務(wù)器,請求讀取數(shù)據(jù)
- DataNode開始傳輸數(shù)據(jù)給客戶端(從磁盤里面讀取數(shù)據(jù)輸入流径玖,以packet為單位)
- 客戶端以packet為單位接收痴脾,先在本地緩存,然后寫入目標(biāo)文件梳星。
SecondaryNameNode
NameNode中的元數(shù)據(jù)是存儲在哪里赞赖?
首先,我們做個假設(shè)冤灾,如果存儲在NameNode節(jié)點(diǎn)的磁盤中前域,因為經(jīng)常需要進(jìn)行隨機(jī)訪問,還有響應(yīng)客戶請求韵吨,必然是效率過低匿垄。因此,元數(shù)據(jù)需要存放在內(nèi)存中归粉。但如果只存在內(nèi)存中椿疗,一旦斷電,元數(shù)據(jù)丟失糠悼,整個集群就無法工作了届榄。因此產(chǎn)生在磁盤中備份元數(shù)據(jù)的FsImage。
這樣又會帶來新的問題绢掰,當(dāng)在內(nèi)存中的元數(shù)據(jù)更新時痒蓬,如果同時更新FsImage童擎,就會導(dǎo)致效率過低滴劲,但如果不更新,就會發(fā)生一致性問題顾复,一旦NameNode節(jié)點(diǎn)斷電班挖,就會產(chǎn)生數(shù)據(jù)丟失。因此芯砸,引入Edits文件(只進(jìn)行追加操作萧芙,效率很高)。每當(dāng)元數(shù)據(jù)有更新或者添加元數(shù)據(jù)時假丧,修改內(nèi)存中的元數(shù)據(jù)并追加到Edits中双揪。這樣,一旦NameNode節(jié)點(diǎn)斷電包帚,可以通過FsImage和Edits的合并渔期,合成元數(shù)據(jù)。
但是,如果長時間添加數(shù)據(jù)到Edits中疯趟,會導(dǎo)致該文件數(shù)據(jù)過大拘哨,效率降低,而且一旦斷電信峻,恢復(fù)元數(shù)據(jù)需要的時間過長倦青。因此,需要定期進(jìn)行FsImage和Edits的合并盹舞,如果這個操作由NameNode節(jié)點(diǎn)完成产镐,又會效率過低。因此踢步,引入一個新的節(jié)點(diǎn)SecondaryNamenode磷账,專門用于FsImage和Edits的合并。
- 第一階段:NameNode啟動
(1)第一次啟動NameNode格式化后贾虽,創(chuàng)建Fsimage和Edits文件逃糟。如果不是第一次啟動,直接加載編輯日志和鏡像文件到內(nèi)存蓬豁。
(2)客戶端對元數(shù)據(jù)進(jìn)行增刪改的請求绰咽。
(3)NameNode記錄操作日志,更新滾動日志地粪。
(4)NameNode在內(nèi)存中對元數(shù)據(jù)進(jìn)行增刪改取募。
第二階段:Secondary NameNode工作
(1)Secondary NameNode詢問NameNode是否需要CheckPoint。直接帶回NameNode是否檢查結(jié)果蟆技。
(2)Secondary NameNode請求執(zhí)行CheckPoint玩敏。
(3)NameNode滾動正在寫的Edits日志。
(4)將滾動前的編輯日志和鏡像文件拷貝到Secondary NameNode质礼。
(5)Secondary NameNode加載編輯日志和鏡像文件到內(nèi)存旺聚,并合并。
(6)生成新的鏡像文件fsimage.chkpoint眶蕉。
(7)拷貝fsimage.chkpoint到NameNode砰粹。
(8)NameNode將fsimage.chkpoint重新命名成fsimage
NN和2NN工作機(jī)制詳解
:
- Fsimage:NameNode內(nèi)存中元數(shù)據(jù)序列化后形成的文件。
- Edits:記錄客戶端更新元數(shù)據(jù)信息的每一步操作(可通過Edits運(yùn)算出元數(shù)據(jù))造挽。
NameNode啟動時碱璃,先滾動Edits并生成一個空的edits.inprogress,然后加載Edits和Fsimage到內(nèi)存中饭入,此時NameNode內(nèi)存就持有最新的元數(shù)據(jù)信息嵌器。Client開始對NameNode發(fā)送元數(shù)據(jù)的增刪改的請求,這些請求的操作首先會被記錄到edits.inprogress中(查詢元數(shù)據(jù)的操作不會被記錄在Edits中谐丢,因為查詢操作不會更改元數(shù)據(jù)信息)爽航,如果此時NameNode掛掉,重啟后會從Edits中讀取元數(shù)據(jù)的信息。然后岳掐,NameNode會在內(nèi)存中執(zhí)行元數(shù)據(jù)的增刪改的操作凭疮。
由于Edits中記錄的操作會越來越多,Edits文件會越來越大串述,導(dǎo)致NameNode在啟動加載Edits時會很慢执解,所以需要對Edits和Fsimage進(jìn)行合并(所謂合并,就是將Edits和Fsimage加載到內(nèi)存中纲酗,照著Edits中的操作一步步執(zhí)行衰腌,最終形成新的Fsimage)。SecondaryNameNode的作用就是幫助NameNode進(jìn)行Edits和Fsimage的合并工作觅赊。
SecondaryNameNode首先會詢問NameNode是否需要CheckPoint(觸發(fā)CheckPoint需要滿足兩個條件中的任意一個右蕊,定時時間到和Edits中數(shù)據(jù)寫滿了)。直接帶回NameNode是否檢查結(jié)果吮螺。
SecondaryNameNode執(zhí)行CheckPoint操作饶囚,首先會讓NameNode滾動Edits并生成一個空的edits.inprogress,滾動Edits的目的是給Edits打個標(biāo)記鸠补,以后所有新的操作都寫入edits.inprogress萝风,其他未合并的Edits和Fsimage會拷貝到SecondaryNameNode的本地,然后將拷貝的Edits和Fsimage加載到內(nèi)存中進(jìn)行合并紫岩,生成fsimage.chkpoint规惰,然后將fsimage.chkpoint拷貝給NameNode,重命名為Fsimage后替換掉原來的Fsimage泉蝌。NameNode在啟動時就只需要加載之前未合并的Edits和Fsimage即可歇万,因為合并過的Edits中的元數(shù)據(jù)信息已經(jīng)被記錄在Fsimage中。
- oiv查看Fsimage文件
(1)查看oiv和oev命令
[atguigu@hadoop102 current] pwd
/opt/module/hadoop-2.7.2/data/tmp/dfs/name/current
[atguigu@hadoop102 current]$ hdfs oiv -p XML -i fsimage_0000000000000000025 -o /opt/module/hadoop-2.7.2/fsimage.xml
[atguigu@hadoop102 current]$ cat /opt/module/hadoop-2.7.2/fsimage.xml
將顯示的xml文件內(nèi)容拷貝到Eclipse中創(chuàng)建的xml文件中勋陪,并格式化贪磺。部分顯示結(jié)果如下。
<inode>
<id>16386</id>
<type>DIRECTORY</type>
<name>user</name>
<mtime>1512722284477</mtime>
<permission>atguigu:supergroup:rwxr-xr-x</permission>
<nsquota>-1</nsquota>
<dsquota>-1</dsquota>
</inode>
<inode>
<id>16387</id>
<type>DIRECTORY</type>
<name>atguigu</name>
<mtime>1512790549080</mtime>
<permission>atguigu:supergroup:rwxr-xr-x</permission>
<nsquota>-1</nsquota>
<dsquota>-1</dsquota>
</inode>
<inode>
<id>16389</id>
<type>FILE</type>
<name>wc.input</name>
<replication>3</replication>
<mtime>1512722322219</mtime>
<atime>1512722321610</atime>
<perferredBlockSize>134217728</perferredBlockSize>
<permission>atguigu:supergroup:rw-r--r--</permission>
<blocks>
<block>
<id>1073741825</id>
<genstamp>1001</genstamp>
<numBytes>59</numBytes>
</block>
</blocks>
</inode >
思考:可以看出粥鞋,F(xiàn)simage中沒有記錄塊所對應(yīng)DataNode缘挽,為什么瞄崇?
在集群啟動后呻粹,要求DataNode上報數(shù)據(jù)塊信息,并間隔一段時間后再次上報苏研。
CheckPoint時間設(shè)置
通常情況下等浊,SecondaryNameNode每隔一小時執(zhí)行一次。
[hdfs-default.xml]
<property>
<name>dfs.namenode.checkpoint.period</name>
<value>3600</value>
</property>
(2)一分鐘檢查一次操作次數(shù)摹蘑,3當(dāng)操作次數(shù)達(dá)到1百萬時筹燕,SecondaryNameNode執(zhí)行一次。
<property>
<name>dfs.namenode.checkpoint.txns</name>
<value>1000000</value>
<description>操作動作次數(shù)</description>
</property>
<property>
<name>dfs.namenode.checkpoint.check.period</name>
<value>60</value>
<description> 1分鐘檢查一次操作次數(shù)</description>
</property >
NameNode故障處理
NameNode故障后,可以采用如下兩種方法恢復(fù)數(shù)據(jù)撒踪。
方法一:將SecondaryNameNode中數(shù)據(jù)拷貝到NameNode存儲數(shù)據(jù)的目錄过咬;
- kill -9 NameNode進(jìn)程
- 刪除NameNode存儲的數(shù)據(jù)(/opt/module/hadoop-2.7.2/data/tmp/dfs/name)
[atguigu@hadoop102 hadoop-2.7.2]$ rm -rf /opt/module/hadoop-2.7.2/data/tmp/dfs/name/* - 拷貝SecondaryNameNode中數(shù)據(jù)到原NameNode存儲數(shù)據(jù)目錄
[atguigu@hadoop102 dfs]$ scp -r atguigu@hadoop104:/opt/module/hadoop-2.7.2/data/tmp/dfs/namesecondary/* ./name/ - 重新啟動NameNode
[atguigu@hadoop102 hadoop-2.7.2]$ sbin/hadoop-daemon.sh start namenode
DataNode
1)一個數(shù)據(jù)塊在DataNode上以文件形式存儲在磁盤上,包括兩個文件制妄,一個是數(shù)據(jù)本身掸绞,一個是元數(shù)據(jù)包括數(shù)據(jù)塊的長度,塊數(shù)據(jù)的校驗和耕捞,以及時間戳衔掸。
2)DataNode啟動后向NameNode注冊,通過后俺抽,周期性(1小時)的向NameNode上報所有的塊信息敞映。
3)心跳是每3秒一次,心跳返回結(jié)果帶有NameNode給該DataNode的命令如復(fù)制塊數(shù)據(jù)到另一臺機(jī)器磷斧,或刪除某個數(shù)據(jù)塊振愿。如果超過10分鐘沒有收到某個DataNode的心跳,則認(rèn)為該節(jié)點(diǎn)不可用弛饭。
4)集群運(yùn)行中可以安全加入和退出一些機(jī)器埃疫。
DataNode節(jié)點(diǎn)保證數(shù)據(jù)完整性的方法。
1)當(dāng)DataNode讀取Block的時候孩哑,它會計算CheckSum栓霜。
2)如果計算后的CheckSum,與Block創(chuàng)建時值不一樣横蜒,說明Block已經(jīng)損壞胳蛮。
3)Client讀取其他DataNode上的Block。
4)DataNode在其文件創(chuàng)建后周期驗證CheckSum
掉線時限參數(shù)設(shè)置
HDFS 2.X新特性
集群間數(shù)據(jù)拷貝
1.scp實現(xiàn)兩個遠(yuǎn)程主機(jī)之間的文件復(fù)制
scp -r hello.txt root@hadoop103:/user/atguigu/hello.txt // 推 push
scp -r root@hadoop103:/user/atguigu/hello.txt hello.txt // 拉 pull
scp -r root@hadoop103:/user/atguigu/hello.txt root@hadoop104:/user/atguigu //是通過本地主機(jī)中轉(zhuǎn)實現(xiàn)兩個遠(yuǎn)程主機(jī)的文件復(fù)制丛晌;如果在兩個遠(yuǎn)程主機(jī)之間ssh沒有配置的情況下可以使用該方式仅炊。
2.采用distcp命令實現(xiàn)兩個Hadoop集群之間的遞歸數(shù)據(jù)復(fù)制
[atguigu@hadoop102 hadoop-2.7.2]$ bin/hadoop distcp
hdfs://haoop102:9000/user/atguigu/hello.txt hdfs://hadoop103:9000/user/atguigu/hello.txt
小文件存檔
案例實操
(1)需要啟動YARN進(jìn)程
[atguigu@hadoop102 hadoop-2.7.2]$ start-yarn.sh
(2)歸檔文件
把/user/atguigu/input目錄里面的所有文件歸檔成一個叫input.har的歸檔文件,并把歸檔后文件存儲到/user/atguigu/output路徑下澎蛛。
[atguigu@hadoop102 hadoop-2.7.2]$ bin/hadoop archive -archiveName input.har –p /user/atguigu/input /user/atguigu/output
(3)查看歸檔
[atguigu@hadoop102 hadoop-2.7.2]$ hadoop fs -lsr /user/atguigu/output/input.har
[atguigu@hadoop102 hadoop-2.7.2]$ hadoop fs -lsr har:///user/atguigu/output/input.har
(4)解歸檔文件
[atguigu@hadoop102 hadoop-2.7.2]$ hadoop fs -cp har:/// user/atguigu/output/input.har/* /user/atguigu
HDFS HA
HDFS-HA工作要點(diǎn)
- 元數(shù)據(jù)管理方式需要改變
內(nèi)存中各自保存一份元數(shù)據(jù)抚垄;
Edits日志只有Active狀態(tài)的NameNode節(jié)點(diǎn)可以做寫操作;
兩個NameNode都可以讀取Edits谋逻;
共享的Edits放在一個共享存儲中管理(qjournal和NFS兩個主流實現(xiàn)) - 需要一個狀態(tài)管理功能模塊
實現(xiàn)了一個zkfailover呆馁,常駐在每一個namenode所在的節(jié)點(diǎn),每一個zkfailover負(fù)責(zé)監(jiān)控自己所在NameNode節(jié)點(diǎn)毁兆,利用zk進(jìn)行狀態(tài)標(biāo)識浙滤,當(dāng)需要進(jìn)行狀態(tài)切換時,由zkfailover來負(fù)責(zé)切換气堕,切換時需要防止brain split現(xiàn)象的發(fā)生纺腊。 - 隔離(Fence)畔咧,即同一時刻僅僅有一個NameNode對外提供服務(wù)
自動故障轉(zhuǎn)移為HDFS部署增加了兩個新組件:ZooKeeper和ZKFailoverController(ZKFC)進(jìn)程,如圖3-20所示揖膜。ZooKeeper是維護(hù)少量協(xié)調(diào)數(shù)據(jù)誓沸,通知客戶端這些數(shù)據(jù)的改變和監(jiān)視客戶端故障的高可用服務(wù)。HA的自動故障轉(zhuǎn)移依賴于ZooKeeper的以下功能:
1)故障檢測:集群中的每個NameNode在ZooKeeper中維護(hù)了一個持久會話壹粟,如果機(jī)器崩潰蔽介,ZooKeeper中的會話將終止,ZooKeeper通知另一個NameNode需要觸發(fā)故障轉(zhuǎn)移煮寡。
2)現(xiàn)役NameNode選擇:ZooKeeper提供了一個簡單的機(jī)制用于唯一的選擇一個節(jié)點(diǎn)為active狀態(tài)虹蓄。如果目前現(xiàn)役NameNode崩潰,另一個節(jié)點(diǎn)可能從ZooKeeper獲得特殊的排外鎖以表明它應(yīng)該成為現(xiàn)役NameNode幸撕。
ZKFC是自動故障轉(zhuǎn)移中的另一個新組件薇组,是ZooKeeper的客戶端,也監(jiān)視和管理NameNode的狀態(tài)坐儿。每個運(yùn)行NameNode的主機(jī)也運(yùn)行了一個ZKFC進(jìn)程律胀,ZKFC負(fù)責(zé)
1)健康監(jiān)測:ZKFC使用一個健康檢查命令定期地ping與之在相同主機(jī)的NameNode,只要該NameNode及時地回復(fù)健康狀態(tài)貌矿,ZKFC認(rèn)為該節(jié)點(diǎn)是健康的炭菌。如果該節(jié)點(diǎn)崩潰,凍結(jié)或進(jìn)入不健康狀態(tài)逛漫,健康監(jiān)測器標(biāo)識該節(jié)點(diǎn)為非健康的黑低。
2)ZooKeeper會話管理:當(dāng)本地NameNode是健康的,ZKFC保持一個在ZooKeeper中打開的會話酌毡。如果本地NameNode處于active狀態(tài)克握,ZKFC也保持一個特殊的znode鎖,該鎖使用了ZooKeeper對短暫節(jié)點(diǎn)的支持枷踏,如果會話終止菩暗,鎖節(jié)點(diǎn)將自動刪除。
3)基于ZooKeeper的選擇:如果本地NameNode是健康的旭蠕,且ZKFC發(fā)現(xiàn)沒有其它的節(jié)點(diǎn)當(dāng)前持有znode鎖停团,它將為自己獲取該鎖。如果成功掏熬,則它已經(jīng)贏得了選舉佑稠,并負(fù)責(zé)運(yùn)行故障轉(zhuǎn)移進(jìn)程以使它的本地NameNode為Active。故障轉(zhuǎn)移進(jìn)程與前面描述的手動故障轉(zhuǎn)移相似孽江,首先如果必要保護(hù)之前的現(xiàn)役NameNode讶坯,然后本地NameNode轉(zhuǎn)換為Active狀態(tài)。
HDFS Federation架構(gòu)設(shè)計
NameNode架構(gòu)的局限性
(1)Namespace(命名空間)的限制
由于NameNode在內(nèi)存中存儲所有的元數(shù)據(jù)(metadata)岗屏,因此單個NameNode所能存儲的對象(文件+塊)數(shù)目受到NameNode所在JVM的heap size的限制辆琅。50G的heap能夠存儲20億(200million)個對象,這20億個對象支持4000個DataNode这刷,12PB的存儲(假設(shè)文件平均大小為40MB)婉烟。隨著數(shù)據(jù)的飛速增長,存儲的需求也隨之增長暇屋。單個DataNode從4T增長到36T似袁,集群的尺寸增長到8000個DataNode。存儲的需求從12PB增長到大于100PB咐刨。
(2)隔離問題
由于HDFS僅有一個NameNode昙衅,無法隔離各個程序,因此HDFS上的一個實驗程序就很有可能影響整個HDFS上運(yùn)行的程序定鸟。
(3)性能的瓶頸
由于是單個NameNode的HDFS架構(gòu)而涉,因此整個HDFS文件系統(tǒng)的吞吐量受限于單個NameNode的吞吐量
能不能有多個NameNode?
不同應(yīng)用可以使用不同NameNode進(jìn)行數(shù)據(jù)管理
圖片業(yè)務(wù)联予、爬蟲業(yè)務(wù)啼县、日志審計業(yè)務(wù)
Hadoop生態(tài)系統(tǒng)中,不同的框架使用不同的NameNode進(jìn)行管理NameSpace沸久。(隔離性)
在hadoop1.0的架構(gòu)中季眷,HDFS的所有的元數(shù)據(jù)都放在一個namenode中,只有一個namespace(名字空間)卷胯。這樣隨著HDFS的數(shù)據(jù)越來越多子刮,單個namenode的資源使用必然會達(dá)到上限,而且namenode的負(fù)載也會越來越高窑睁,限制了HDFS的性能话告。
在hadoop2.0架構(gòu)中,namenode federation(聯(lián)合)通過多個namenode/namespace把元數(shù)據(jù)的存儲和管理分散到多個節(jié)點(diǎn)中卵慰,使到namenode/namespace可以通過增加機(jī)器來進(jìn)行水平擴(kuò)展沙郭,并且能把單個namenode的負(fù)載分散到多個節(jié)點(diǎn)中,在HDFS數(shù)據(jù)規(guī)模較大的時候不會也降低HDFS的性能裳朋。還有可以通過多個namespace來隔離不同類型的應(yīng)用病线,把不同類型應(yīng)用的HDFS元數(shù)據(jù)的存儲和管理分派到不同的namenode中。
如果上圖所示鲤嫡,一個block pool由屬于同一個namespace的數(shù)據(jù)塊組成送挑,每個namenode管理一個namespace,即每個namenode負(fù)責(zé)存儲和管理一個block pool的元數(shù)據(jù)暖眼。而每個datanode是會連接所有的namenode的惕耕,為所有的block pools所共享,即每個datanode都會存儲所有的block pools的數(shù)據(jù)塊诫肠。每個block pool通過namespace隔離開來司澎,對一個block pool的操作不會影響另外一個block pool欺缘。
從配置和使用的角度來看,整個HDFS有一個唯一的clusterid挤安,如“hellokitty”谚殊,它可以配置多個block pool/namespace(也叫name service),如“mycluster”和“yourcluster”蛤铜。為了方便訪問不同名字空間的目錄和文件嫩絮,federation還提供了一個類似linux的Client Side Mount Table的掛載機(jī)制,提供了一個統(tǒng)一的全局的文件系統(tǒng)視圖(viewfs)围肥。用戶可以根據(jù)自己的需要把各個namespace掛載到一個叫做viewFS的文件系統(tǒng)視圖的不同目錄下剿干。例如namespace/name service “mycluster”和“yourcluster”分別掛載到viewfs的“/my”和“/your”目錄下,如下圖所示:
federation和HA
上面提到的每個namespace/name service配置一個namenode穆刻,這樣這個namespace/name service的單點(diǎn)問題還是存在置尔,因此可以給每個namespace/name service配置成HA。
假設(shè)我們有4臺namenode蛹批,分別是namenode1撰洗,namenode2,namenode3腐芍,namenode4差导。其中namenode1和namenode2是namespace/name service“mycluster”的兩個主備namenode節(jié)點(diǎn),NN_ID分別是“mycluster”的“nn1”和“nn2”猪勇;而namenode3和namenode4是namespace/name service“yourcluster”的兩個主備namenode節(jié)點(diǎn)设褐,NN_ID分別是“yourcluster”的“nn1”和“nn2”。
“mycluster”和“yourcluster”分別掛載在viewfs的“/my”和“/your”目錄下泣刹。
一般1000臺機(jī)器一下的中小規(guī)模的hadoop集群助析,一個namespace/name service就足夠了,不需要考慮federation椅您,以免增加不必要的復(fù)雜性外冀。