HDFS文件系統(tǒng)

摘要:當(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í)一些事件的主要順序瞧预。


圖3-1 HDFS讀文件過(guò)程
  1. 客戶端通過(guò)調(diào)用FileSystem對(duì)象的open()來(lái)讀取希望打開的文件屎债。對(duì)于HDFS來(lái)說(shuō),這個(gè)對(duì)象是分布式文件系統(tǒng)的一個(gè)實(shí)例垢油。
  2. 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ì)象正压。
  3. 接著client對(duì)這個(gè)輸入流調(diào)用read()。存儲(chǔ)著文件開頭部分塊的數(shù)據(jù)節(jié)點(diǎn)地址的DFSInputStream隨即與這些塊最近的datanode相連接责球。
  4. 通過(guò)在數(shù)據(jù)流中反復(fù)調(diào)用read()焦履,數(shù)據(jù)會(huì)從datanode返回client。
  5. 到達(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的副本灸叼。


圖3-2 HDFS寫文件過(guò)程
  1. 客戶端通過(guò)在DistributedFileSystem中調(diào)用create()來(lái)創(chuàng)建文件。
  2. 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之間的通信唱歧。
  3. 在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)在管線中。
  4. 數(shù)據(jù)流將包分流給管線中第一個(gè)的datanode尖滚,這個(gè)節(jié)點(diǎn)會(huì)存儲(chǔ)包并且發(fā)送給管線中的第二個(gè)datanode喉刘。同樣地,第二個(gè)datanode存儲(chǔ)包并且傳給管線中的第三個(gè)數(shù)據(jù)節(jié)點(diǎn)漆弄。
  5. 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)呢撞。
  6. client完成數(shù)據(jù)的寫入后,就會(huì)在流中調(diào)用close()饰剥。
  7. 在向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的文件操作工具類派哲。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末臼氨,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子芭届,更是在濱河造成了極大的恐慌一也,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,919評(píng)論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件喉脖,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡抑月,警方通過(guò)查閱死者的電腦和手機(jī)树叽,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,567評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)谦絮,“玉大人题诵,你說(shuō)我怎么就攤上這事〔阒澹” “怎么了性锭?”我有些...
    開封第一講書人閱讀 163,316評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)叫胖。 經(jīng)常有香客問(wèn)我草冈,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,294評(píng)論 1 292
  • 正文 為了忘掉前任怎棱,我火速辦了婚禮哩俭,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘拳恋。我一直安慰自己凡资,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,318評(píng)論 6 390
  • 文/花漫 我一把揭開白布谬运。 她就那樣靜靜地躺著隙赁,像睡著了一般。 火紅的嫁衣襯著肌膚如雪梆暖。 梳的紋絲不亂的頭發(fā)上伞访,一...
    開封第一講書人閱讀 51,245評(píng)論 1 299
  • 那天,我揣著相機(jī)與錄音式廷,去河邊找鬼咐扭。 笑死,一個(gè)胖子當(dāng)著我的面吹牛滑废,可吹牛的內(nèi)容都是我干的蝗肪。 我是一名探鬼主播,決...
    沈念sama閱讀 40,120評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼蠕趁,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼薛闪!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起俺陋,我...
    開封第一講書人閱讀 38,964評(píng)論 0 275
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤豁延,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后腊状,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體诱咏,經(jīng)...
    沈念sama閱讀 45,376評(píng)論 1 313
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,592評(píng)論 2 333
  • 正文 我和宋清朗相戀三年缴挖,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了袋狞。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,764評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡映屋,死狀恐怖苟鸯,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情棚点,我是刑警寧澤早处,帶...
    沈念sama閱讀 35,460評(píng)論 5 344
  • 正文 年R本政府宣布,位于F島的核電站瘫析,受9級(jí)特大地震影響砌梆,放射性物質(zhì)發(fā)生泄漏默责。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,070評(píng)論 3 327
  • 文/蒙蒙 一么库、第九天 我趴在偏房一處隱蔽的房頂上張望傻丝。 院中可真熱鬧,春花似錦诉儒、人聲如沸葡缰。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,697評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)泛释。三九已至,卻和暖如春温算,著一層夾襖步出監(jiān)牢的瞬間怜校,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,846評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工注竿, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留茄茁,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,819評(píng)論 2 370
  • 正文 我出身青樓巩割,卻偏偏與公主長(zhǎng)得像裙顽,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子宣谈,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,665評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容