摘要:當(dāng)數(shù)據(jù)集超出一臺(tái)物理計(jì)算機(jī)的存儲(chǔ)能力量時(shí)僧界,便有必要將它分布到多個(gè)獨(dú)立的計(jì)算機(jī)。管理著跨計(jì)算機(jī)網(wǎng)絡(luò)存儲(chǔ)的文件系統(tǒng)稱為分布式文件系統(tǒng)械筛。HDFS全稱為Hadoop Distributed Filesystem捎泻,是Hadoop的分布式文件系統(tǒng),也是當(dāng)下開源社區(qū)最受歡迎的分布式文件系統(tǒng)埋哟。本文主要介紹了HDFS的設(shè)計(jì)、概念郎汪、文件讀寫原理等基礎(chǔ)知識(shí)赤赊,并給出了HDFS各類文件操作的Java實(shí)現(xiàn)。
1. HDFS的設(shè)計(jì)
理念一:超大文件
HDFS面向存儲(chǔ)幾百M(fèi)B煞赢,幾百GB甚至幾百TB大小的大文件抛计,這是傳統(tǒng)的本地文件系統(tǒng)所不及的。
理念二:流式數(shù)據(jù)訪問(wèn)
流式數(shù)據(jù)訪問(wèn),就是一次寫入,多次讀取晦雨,不可以修改數(shù)據(jù)钓株,只能刪除數(shù)據(jù)。那么吊骤,為什么寫入是一次性的,不可以再修改呢?在學(xué)完HDFS文件讀寫原理后懦铺,我們?cè)賮?lái)探討其原因。
理念三:商用硬件
HDFS設(shè)計(jì)理念之一就是讓它能運(yùn)行在普通的硬件之上支鸡,即便硬件出現(xiàn)故障冬念,也可以通過(guò)容錯(cuò)策略來(lái)保證數(shù)據(jù)的高可用。
不適合一:低延遲讀寫
由于HDFS主要是為達(dá)到高的數(shù)據(jù)吞吐量而設(shè)計(jì)的牧挣,所以其延遲較高急前,對(duì)于那些有低延時(shí)要求的應(yīng)用程序,HBase是一個(gè)更好的選擇瀑构。
不適合二:存儲(chǔ)大量小文件
因?yàn)閚amenode把文件系統(tǒng)的元數(shù)據(jù)放置在內(nèi)存中裆针,所以文件系統(tǒng)所能容納的文件數(shù)目是由namenode的內(nèi)存大小來(lái)決定。一般來(lái)說(shuō)检碗,每一個(gè)文件据块、文件夾和Block需要占據(jù)150字節(jié)左右的空間,所以折剃,如果你有100萬(wàn)個(gè)文件另假,每一個(gè)占據(jù)一個(gè)Block,你就至少需要300MB內(nèi)存怕犁。當(dāng)前來(lái)說(shuō)边篮,數(shù)百萬(wàn)的文件還是可行的己莺,當(dāng)擴(kuò)展到數(shù)十億時(shí),對(duì)于當(dāng)前的硬件水平來(lái)說(shuō)就沒(méi)法實(shí)現(xiàn)了戈轿。HBase利用SequenceFile凌受、MapFile、Har等方式歸檔小文件思杯,同時(shí)胜蛉,保存歸檔文件的映射關(guān)系,從而解決了HDFS不適合存儲(chǔ)大量小文件的難題色乾。
不適合三:多用戶寫入和任意修改文件
在HDFS的一個(gè)文件中只有一個(gè)寫入者誊册,而且寫操作只能在文件末尾完成,即只能執(zhí)行追加操作暖璧。目前HDFS還不支持多個(gè)用戶對(duì)同一文件的寫操作案怯,以及在文件任意位置進(jìn)行修改。其原因澎办,我們會(huì)在后面進(jìn)行探討嘲碱。
2. HDFS的概念
2.1 塊
在《操作系統(tǒng)原理》中就有磁盤塊的概念,它是操作系統(tǒng)中最小的邏輯存儲(chǔ)單位局蚀,可以屏蔽底層硬件存儲(chǔ)結(jié)構(gòu)的復(fù)雜性麦锯,為上層應(yīng)用提供統(tǒng)一的存儲(chǔ)結(jié)構(gòu)和編程規(guī)范。而HDFS引入了塊的概念至会,且默認(rèn)塊的大小為64MB离咐,它比磁盤塊大的多,其原因主要是為了減小塊尋址操作在整個(gè)塊操作中的時(shí)間比奉件。
在分布式文件系統(tǒng)中使用抽象塊可以帶來(lái)很多好處:
好處一:可存儲(chǔ)超大文件
對(duì)于一個(gè)超大文件宵蛀,可以通過(guò)將文件分成多個(gè)塊,每個(gè)分塊可以存儲(chǔ)到集群的任意一個(gè)磁盤县貌,最后术陶,通過(guò)整合文件的分塊來(lái)恢復(fù)文件。
好處二:簡(jiǎn)化存儲(chǔ)子系統(tǒng)
因?yàn)閴K的大小固定煤痕,計(jì)算一個(gè)磁盤能存多少塊就相對(duì)容易梧宫。另外,塊只是一部分存儲(chǔ)的數(shù)據(jù)摆碉,對(duì)于文件的元數(shù)據(jù)塘匣,不需要與塊一同存儲(chǔ),其他系統(tǒng)可以正交地管理元數(shù)據(jù)巷帝。
好處三:利于數(shù)據(jù)的備份
塊很適合于為提供容錯(cuò)和實(shí)用性而做的復(fù)制操作忌卤。如果一個(gè)塊損壞了,系統(tǒng)可以在其它地方讀取另一個(gè)副本(在Hadoop搭建時(shí)可以設(shè)置塊備份數(shù)量楞泼,搭建方法可參考文章大數(shù)據(jù)之路起航——大數(shù)據(jù)環(huán)境搭建)驰徊,從而保證了數(shù)據(jù)的完整性笤闯。
2.2 名稱節(jié)點(diǎn)和數(shù)據(jù)節(jié)點(diǎn)
HDFS集群有兩種節(jié)點(diǎn),以管理者-工作者的模式運(yùn)行棍厂,即一個(gè)名稱節(jié)點(diǎn)(管理者)和多個(gè)數(shù)據(jù)節(jié)點(diǎn)(工作者)颗味。
名稱節(jié)點(diǎn)(NameNode)
NameNode負(fù)責(zé)管理文件目錄、文件和Block的對(duì)應(yīng)關(guān)系以及Block和數(shù)據(jù)節(jié)點(diǎn)(DataNode)的對(duì)應(yīng)關(guān)系牺弹。
數(shù)據(jù)節(jié)點(diǎn)(DataNode)
DataNode負(fù)責(zé)存儲(chǔ)數(shù)據(jù)塊浦马,同時(shí),在被用戶或NameNode調(diào)用時(shí)提供塊定位服務(wù)张漂。DataNode還定時(shí)向NameNode發(fā)送它們存儲(chǔ)的塊的列表捐韩。
二級(jí)名稱節(jié)點(diǎn)(SecondryNameNode)
負(fù)責(zé)合并NameNode的edit logs到fsimage文件中。
https://blog.csdn.net/xh16319/article/details/31375197
3. HDFS數(shù)據(jù)流
3.1 文件讀取剖析
為了了解客戶端及與之交互NameNode和DataNode之間的數(shù)據(jù)流是怎樣的鹃锈,我們可以參考圖3-1,其中顯示了在讀取文件時(shí)一些事件的主要順序瞧预。
- 客戶端通過(guò)調(diào)用FileSystem對(duì)象的open()來(lái)讀取希望打開的文件屎债。對(duì)于HDFS來(lái)說(shuō),這個(gè)對(duì)象是分布式文件系統(tǒng)的一個(gè)實(shí)例垢油。
- DistributedFileSystem通過(guò)RPC(Remote Procedure Call)通信協(xié)議來(lái)遠(yuǎn)程調(diào)用namenode盆驹,以確定文件的開頭部分的塊位置。對(duì)于每一塊滩愁,namenode返回具有該塊副本的datanode地址躯喇。此外,這些datanode根據(jù)他們與client的距離來(lái)排序(根據(jù)網(wǎng)絡(luò)集群的拓?fù)洌┫跬鳌H绻揷lient本身就是一個(gè)datanode廉丽,便從本地datanode中讀取。DistributedFileSystem 返回一個(gè)FSDataInputStream對(duì)象給client讀取數(shù)據(jù)妻味,F(xiàn)SDataInputStream轉(zhuǎn)而包裝了一個(gè)DFSInputStream對(duì)象正压。
- 接著client對(duì)這個(gè)輸入流調(diào)用read()。存儲(chǔ)著文件開頭部分塊的數(shù)據(jù)節(jié)點(diǎn)地址的DFSInputStream隨即與這些塊最近的datanode相連接责球。
- 通過(guò)在數(shù)據(jù)流中反復(fù)調(diào)用read()焦履,數(shù)據(jù)會(huì)從datanode返回client。
- 到達(dá)塊的末端時(shí)雏逾,DFSInputStream會(huì)關(guān)閉與datanode間的聯(lián)系嘉裤,然后為下一個(gè)塊找到最佳的datanode。client端只需要讀取一個(gè)連續(xù)的流栖博,這些對(duì)于client來(lái)說(shuō)都是透明的屑宠。
另外,在讀取的時(shí)候笛匙,如果client與datanode通信時(shí)遇到一個(gè)錯(cuò)誤侨把,那么它就會(huì)去嘗試對(duì)這個(gè)塊來(lái)說(shuō)下一個(gè)最近的塊犀变。它也會(huì)記住那個(gè)故障節(jié)點(diǎn)的datanode,以保證不會(huì)再對(duì)之后的塊進(jìn)行徒勞無(wú)益的嘗試秋柄。client也會(huì)確認(rèn)datanode發(fā)來(lái)的數(shù)據(jù)的校驗(yàn)和获枝。如果發(fā)現(xiàn)一個(gè)損壞的塊,它就會(huì)在client試圖從別的datanode中讀取一個(gè)塊的副本之前報(bào)告給namenode骇笔。這個(gè)設(shè)計(jì)的一個(gè)重點(diǎn)是省店,client直接聯(lián)系datanode去檢索數(shù)據(jù),并被namenode指引到塊中最好的datanode笨触。因?yàn)閿?shù)據(jù)流在此集群中是在所有datanode分散進(jìn)行的懦傍。所以這種設(shè)計(jì)能使HDFS可擴(kuò)展到最大的并發(fā)client數(shù)量。同時(shí)芦劣,namenode只不過(guò)提供塊的位置請(qǐng)求(存儲(chǔ)在內(nèi)存中粗俱,十分高效),不是提供數(shù)據(jù)虚吟。否則如果客戶端數(shù)量增長(zhǎng)寸认,namenode會(huì)快速的成為一個(gè)“瓶頸”。
3.2 文件寫入剖析
客戶端要向HDFS寫數(shù)據(jù)串慰,首先要跟namenode通信以確認(rèn)可以寫文件并獲得接收文件block的datanode偏塞,然后,客戶端按順序?qū)⑽募lock逐個(gè)傳遞給相應(yīng)datanode邦鲫,并由接收到block的datanode負(fù)責(zé)向其他datanode復(fù)制block的副本灸叼。
- 客戶端通過(guò)在DistributedFileSystem中調(diào)用create()來(lái)創(chuàng)建文件。
- DistributedFileSystem 使用RPC去調(diào)用namenode庆捺,在文件系統(tǒng)的命名空間創(chuàng)一個(gè)新的文件古今,沒(méi)有塊與之相聯(lián)系。namenode執(zhí)行各種不同的檢查以確保這個(gè)文件不會(huì)已經(jīng)存在疼燥,并且在client有可以創(chuàng)建文件的適當(dāng)?shù)脑S可沧卢。如果檢查通過(guò),namenode就會(huì)生成一個(gè)新的文件記錄醉者;否則但狭,文件創(chuàng)建失敗并向client拋出一個(gè)IOException異常。分布式文件系統(tǒng)返回一個(gè)文件系統(tǒng)數(shù)據(jù)輸出流撬即,讓client開始寫入數(shù)據(jù)立磁。就像讀取事件一樣,文件系統(tǒng)數(shù)據(jù)輸出流控制一個(gè)DFSOutputStream剥槐,負(fù)責(zé)處理datanode和namenode之間的通信唱歧。
- 在client寫入數(shù)據(jù)時(shí),DFSOutputStream將它分成一個(gè)個(gè)的包,寫入內(nèi)部隊(duì)列颅崩,稱為數(shù)據(jù)隊(duì)列几于。數(shù)據(jù)流處理數(shù)據(jù)隊(duì)列,數(shù)據(jù)流的責(zé)任是根據(jù)適合的datanode的列表要求namenode分配適合的新塊來(lái)存儲(chǔ)數(shù)據(jù)副本沿后。這一組datanode列表形成一個(gè)管線————假設(shè)副本數(shù)是3沿彭,所以有3個(gè)節(jié)點(diǎn)在管線中。
- 數(shù)據(jù)流將包分流給管線中第一個(gè)的datanode尖滚,這個(gè)節(jié)點(diǎn)會(huì)存儲(chǔ)包并且發(fā)送給管線中的第二個(gè)datanode喉刘。同樣地,第二個(gè)datanode存儲(chǔ)包并且傳給管線中的第三個(gè)數(shù)據(jù)節(jié)點(diǎn)漆弄。
- DFSOutputStream也有一個(gè)內(nèi)部的數(shù)據(jù)包隊(duì)列來(lái)等待datanode收到確認(rèn)睦裳,稱為確認(rèn)隊(duì)列。一個(gè)包只有在被管線中所有的節(jié)點(diǎn)確認(rèn)后才會(huì)被移除出確認(rèn)隊(duì)列撼唾。如果在有數(shù)據(jù)寫入期間廉邑,datanode發(fā)生故障, 則會(huì)執(zhí)行下面的操作倒谷,當(dāng)然這對(duì)寫入數(shù)據(jù)的client而言是透明的鬓催。首先管線被關(guān)閉,確認(rèn)隊(duì)列中的任何包都會(huì)被添加回?cái)?shù)據(jù)隊(duì)列的前面恨锚,以確保故障節(jié)點(diǎn)下游的datanode不會(huì)漏掉任意一個(gè)包。為存儲(chǔ)在另一正常datanode的當(dāng)前數(shù)據(jù)塊制定一個(gè)新的標(biāo)識(shí)倍靡,并將該標(biāo)識(shí)傳給namenode猴伶,以便故障節(jié)點(diǎn)datanode在恢復(fù)后可以刪除存儲(chǔ)的部分?jǐn)?shù)據(jù)塊。從管線中刪除故障數(shù)據(jù)節(jié)點(diǎn)并且把余下的數(shù)據(jù)塊寫入管線中的兩個(gè)正常的datanode塌西。namenode注意到塊復(fù)本量不足時(shí)他挎,會(huì)在另一個(gè)節(jié)點(diǎn)上創(chuàng)建一個(gè)新的復(fù)本。后續(xù)的數(shù)據(jù)塊繼續(xù)正常接收處理捡需。只要dfs.replication.min的副本(默認(rèn)是1)被寫入办桨,寫操作就是成功的,并且這個(gè)塊會(huì)在集群中被異步復(fù)制站辉,直到其滿足目標(biāo)副本數(shù)(dfs.replication 默認(rèn)值為3)呢撞。
- client完成數(shù)據(jù)的寫入后,就會(huì)在流中調(diào)用close()饰剥。
- 在向namenode節(jié)點(diǎn)發(fā)送完消息之前殊霞,此方法會(huì)將余下的所有包放入datanode管線并等待確認(rèn)。namenode節(jié)點(diǎn)已經(jīng)知道文件由哪些塊組成(通過(guò)Data streamer 詢問(wèn)塊分配)汰蓉,所以它只需在返回成功前等待塊進(jìn)行最小量的復(fù)制绷蹲。
復(fù)本的布局需要對(duì)可靠性、寫入帶寬和讀取帶寬進(jìn)行權(quán)衡。Hadoop的默認(rèn)布局策略是在運(yùn)行客戶端的節(jié)點(diǎn)上放第1個(gè)復(fù)本(如果客戶端運(yùn)行在集群之外祝钢,就隨機(jī)選擇一個(gè)節(jié)點(diǎn)比规,不過(guò)系統(tǒng)會(huì)避免挑選那些存儲(chǔ)太滿或太忙的節(jié)點(diǎn)。)第2個(gè)復(fù)本放在與第1個(gè)復(fù)本不同且隨機(jī)另外選擇的機(jī)架的節(jié)點(diǎn)上(離架)拦英。第3個(gè)復(fù)本與第2個(gè)復(fù)本放在相同的機(jī)架蜒什,且隨機(jī)選擇另一個(gè)節(jié)點(diǎn)。其他復(fù)本放在集群中隨機(jī)的節(jié)點(diǎn)上龄章,不過(guò)系統(tǒng)會(huì)盡量避免相同的機(jī)架放太多復(fù)本吃谣。總的來(lái)說(shuō)做裙,這一方法不僅提供了很好的穩(wěn)定性(數(shù)據(jù)塊存儲(chǔ)在兩個(gè)機(jī)架中)并實(shí)現(xiàn)很好的負(fù)載均衡岗憋,包括寫入帶寬(寫入操作只需要遍歷一個(gè)交換機(jī))、讀取性能(可以從兩個(gè)機(jī)架中選擇讀让)和集群中塊的均勻分布(客戶端只在本地機(jī)架上寫入一個(gè)塊)仔戈。
4. Java應(yīng)用程序代碼
首先,需要在maven中引入Hadoop的依賴拧廊,版本需要根據(jù)安裝的hadoop版本而定监徘,作者的hadoop版本是2.7.6。
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-client</artifactId>
<version>2.7.6</version>
</dependency>
然后吧碾,創(chuàng)建HDFSConfig類凰盔,該類負(fù)責(zé)配置連接信息,并連接HDFS倦春。
package com.whut.config;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileSystem;
import java.io.IOException;
public class HDFSConfig {
private static FileSystem fileSystem = null;
static {
Configuration conf = new Configuration();
conf.set("fs.defaultFS","hdfs://localhost:9000");
conf.setBoolean("dfs.support.append", true);
conf.set("dfs.client.block.write.replace-datanode-on-failure.policy", "NEVER");
conf.set("dfs.client.block.write.replace-datanode-on-failure.enable", "true");
try {
fileSystem = FileSystem.get(conf);
} catch (IOException e) {
e.printStackTrace();
}
}
public static FileSystem getFileSystem() {
return fileSystem;
}
public static void close() {
try {
fileSystem.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
最后户敬,創(chuàng)建HDFS文件操作工具類,具體包括:創(chuàng)建文件夾睁本、列出文件夾尿庐、文件上傳、文件下載呢堰、寫文件(追加和覆蓋模式)抄瑟、列出文件、刪除文件枉疼、讀文件等皮假。
package com.whut.util;
import com.whut.config.HDFSConfig;
import org.apache.hadoop.fs.*;
import org.apache.hadoop.io.IOUtils;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;
/**
* Created by 楊赟 on 2018-07-18.
*/
public class HDFSUtil {
/**
* @since 2018-07-18
* @author 楊赟
* @describe 上傳本地文件到HDFS
* @param local 本地路徑
* @param hdfs DFS路徑
* @return void
*/
public static void upload(String local, String hdfs){
FileSystem fs = HDFSConfig.getFileSystem();
if(fs == null)
return;
try {
fs.copyFromLocalFile(new Path(local),new Path(hdfs));
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* @since 2018-07-18
* @author 楊赟
* @describe 將hdfs上文件下載到本地
* @param hdfs HDFS路徑
* @param local 本地路徑
* @return void
*/
public static void download(String hdfs, String local){
FileSystem fs = HDFSConfig.getFileSystem();
if(fs == null)
return;
try {
fs.copyToLocalFile(new Path(hdfs), new Path(local));
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* @since 2018-07-18
* @author 楊赟
* @describe 在hdfs目錄下面創(chuàng)建文件夾
* @param name HDFS文件夾名
* @return void
*/
public static void createDir(String name){
FileSystem fs = HDFSConfig.getFileSystem();
if(fs == null)
return;
try {
fs.mkdirs(new Path(name));
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* @since 2018-07-18
* @author 楊赟
* @describe 創(chuàng)建新的文件
* @param name 文件名
* @param content 內(nèi)容
* @return void
*/
public static void createFile(String name, String content) {
FileSystem fs = HDFSConfig.getFileSystem();
if(fs == null)
return;
FSDataOutputStream os = null;
try {
os = fs.create(new Path(name));
os.write(content.getBytes());
os.close();
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* @since 2018-07-18
* @author 楊赟
* @describe 向文件寫入新的數(shù)據(jù)
* @param name 文件名
* @param content 內(nèi)容
* @return void
*/
public static void append(String name, String content){
FileSystem fs = HDFSConfig.getFileSystem();
if(fs == null)
return;
try {
if (fs.exists(new Path(name))) {
try {
InputStream in = new ByteArrayInputStream(content.getBytes());
OutputStream out = fs.append(new Path(name));
IOUtils.copyBytes(in, out, 4096, true);
out.close();
in.close();
} catch (Exception e) {
e.printStackTrace();
}
} else {
createFile(name, content);
}
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* @since 2018-07-18
* @author 楊赟
* @describe 刪除hdfs上的文件或文件夾
* @param name 文件或文件夾名
* @return void
*/
public static void remove(String name) {
FileSystem fs = HDFSConfig.getFileSystem();
if(fs == null)
return;
try {
fs.delete(new Path(name),true);
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* @since 2018-07-18
* @author 楊赟
* @describe 列出文件
* @param path HDFS文件夾名
* @return 文件列表
*/
public static List<String> listFile(String path) {
List<String> list = new ArrayList<>();
FileSystem fs = HDFSConfig.getFileSystem();
if(fs == null)
return list;
FileStatus[] fileStatuses = new FileStatus[0];
try {
fileStatuses = fs.listStatus(new Path(path));
} catch (IOException e) {
e.printStackTrace();
}
for (FileStatus fileStatus : fileStatuses) {
if(fileStatus.isFile())
list.add(path);
}
return list;
}
/**
* @since 2018-07-18
* @author 楊赟
* @describe 列出文件夾
* @param path HDFS文件夾名
* @return 文件夾列表
*/
public static List<String> listDir(String path){
List<String> list = new ArrayList<>();
FileSystem fs = HDFSConfig.getFileSystem();
if(fs == null)
return list;
FileStatus[] fileStatuses = new FileStatus[0];
try {
fileStatuses = fs.listStatus(new Path(path));
} catch (IOException e) {
e.printStackTrace();
}
for (FileStatus fileStatus : fileStatuses) {
if(fileStatus.isDirectory())
list.add(path);
}
return list;
}
/**
* @since 2018-07-18
* @author 楊赟
* @describe 讀文件
* @param name 文件名
* @return byte[]
*/
public static byte[] readFile(String name){
FileSystem fs = HDFSConfig.getFileSystem();
if(fs == null)
return null;
Path path = new Path(name);
try {
if (fs.exists(path)) {
FSDataInputStream is = fs.open(path);
FileStatus stat = fs.getFileStatus(path);
byte[] buffer = new byte[Integer.parseInt(String.valueOf(stat.getLen()))];
is.readFully(0, buffer);
is.close();
return buffer;
}
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
/**
* @since 2018-07-18
* @author 楊赟
* @describe 文件操作測(cè)試
*/
public static void main(String[] args) {
HDFSUtil.download("/data/output.csv","/home/ubuntu/桌面");
}
}
5. Hadoop采用流數(shù)據(jù)訪問(wèn)模式的原因
現(xiàn)在,我們可以來(lái)總結(jié)一下Hadoop采用流數(shù)據(jù)訪問(wèn)模式的原因了骂维。
原因一:寫性能低于讀性能
因?yàn)閔adoop在寫數(shù)據(jù)的時(shí)候要在namenode上進(jìn)行meta元素的寫入钞翔,與此同時(shí)還需要將數(shù)據(jù)寫入到datanode上,如果是大型的數(shù)據(jù)席舍,文件會(huì)分成很多個(gè)區(qū)塊布轿。這樣在meta數(shù)據(jù)和塊數(shù)據(jù)的寫入時(shí)會(huì)產(chǎn)生大量的消耗。相對(duì)于讀取操作來(lái)說(shuō),就會(huì)簡(jiǎn)單很多汰扭,只需要在namenode上查詢到數(shù)據(jù)的meta信息稠肘,接著就不關(guān)namenode的事了。從而為namenode省下大量資源來(lái)進(jìn)行其他的操作萝毛。
原因二:互斥鎖影響性能
可以試想一下项阴,當(dāng)有多個(gè)用戶同時(shí)寫數(shù)據(jù)時(shí),那么就要給寫操作加互斥鎖笆包,而鎖會(huì)大幅度降低性能环揽。所以為了簡(jiǎn)單,就不能修改了庵佣,這是非常暴力但是卻很有效的手段歉胶。
原因三:大文件修改意義不大
我們進(jìn)行大數(shù)據(jù)分析時(shí),并不會(huì)經(jīng)常性的修改原始數(shù)據(jù)巴粪,而且對(duì)于大文件來(lái)說(shuō)通今,想要在文件中定位位置是非常耗費(fèi)時(shí)間的,還不如直接刪除重新寫入肛根。
6. 總結(jié)
本文主要參考了《Hadoop權(quán)威指南》辫塌,對(duì)HDFS中相對(duì)基礎(chǔ)的知識(shí)點(diǎn)進(jìn)行了歸納總結(jié)。并通過(guò)Java編程實(shí)現(xiàn)了HDFS的文件操作工具類派哲。