Hadoop體系中,hive和hbase的區別,那么什么又是hdfs呢?


首先理清hive和hbase的概念吧:

1、hive是什么?
hive可以認為是map-reduce的一個包裝。
hive的意義就是把好寫的hive的sql(也叫hql)轉換為復雜難寫的map-reduce程序,從而降低使用Hadoop中使用map-reduce的難度。
Hive本身不存儲和計算數據,它完全依賴於HDFS和MapReduce,Hive中的表純邏輯(只是個邏輯表)

2、hbase是什么?
hbase可以認為是hdfs的一個包裝。他的本質是數據存儲,是個NoSql數據庫;hbase部署於hdfs之上,並且克服了hdfs在隨機讀寫方面的缺點。
hbase可以理解為為hdfs建立了索引,查詢不走map-reduce,直接走自己的表
hbase是物理表,不是邏輯表,提供一個超大的內存hash表,搜索引擎通過它來存儲索引,方便查詢操作。

深入理解HDFS:Hadoop分布式文件系統

文本詳細介紹了HDFS中的許多概念,對於理解Hadoop分布式文件系統很有幫助。

1. 介紹

在現代的企業環境中,單機容量往往無法存儲大量數據,需要跨機器存儲。統一管理分布在集群上的文件系統稱為分布式文件系統。而一旦在系統中,引入網絡,就不可避免地引入了所有網絡編程的復雜性,例如挑戰之一是如果保證在節點不可用的時候數據不丟失。

傳統的網絡文件系統(NFS)雖然也稱為分布式文件系統,但是其存在一些限制。由於NFS中,文件是存儲在單機上,因此無法提供可靠性保證,當很多客戶端同時訪問NFS Server時,很容易造成服務器壓力,造成性能瓶頸。另外如果要對NFS中的文件中進行操作,需要首先同步到本地,這些修改在同步到服務端之前,其他客戶端是不可見的。某種程度上,NFS不是一種典型的分布式系統,雖然它的文件的確放在遠端(單一)的服務器上面。

這里寫圖片描述

這里寫圖片描述

從NFS的協議棧可以看到,它事實上是一種VFS(操作系統對文件的一種抽象)實現。

HDFS,是Hadoop Distributed File System的簡稱,是Hadoop抽象文件系統的一種實現。Hadoop抽象文件系統可以與本地系統、Amazon S3等集成,甚至可以通過Web協議(webhsfs)來操作。HDFS的文件分布在集群機器上,同時提供副本進行容錯及可靠性保證。例如客戶端寫入讀取文件的直接操作都是分布在集群各個機器上的,沒有單點性能壓力。

如果你從零開始搭建一個完整的集群,參考[Hadoop集群搭建詳細步驟(2.6.0)](http://blog.csdn.net/bingduanlbd/article/details/51892750

2. HDFS設計原則

HDFS設計之初就非常明確其應用場景,適用與什么類型的應用,不適用什么應用,有一個相對明確的指導原則。

2.1 設計目標

  • 存儲非常大的文件:這里非常大指的是幾百M、G、或者TB級別。實際應用中已有很多集群存儲的數據達到PB級別。根據Hadoop官網,Yahoo!的Hadoop集群約有10萬顆CPU,運行在4萬個機器節點上。更多世界上的Hadoop集群使用情況,參考Hadoop官網.

  • 采用流式的數據訪問方式: HDFS基於這樣的一個假設:最有效的數據處理模式是一次寫入、多次讀取數據集經常從數據源生成或者拷貝一次,然后在其上做很多分析工作
    分析工作經常讀取其中的大部分數據,即使不是全部。 因此讀取整個數據集所需時間比讀取第一條記錄的延時更重要。

  • 運行於商業硬件上: Hadoop不需要特別貴的、reliable的機器,可運行於普通商用機器(可以從多家供應商采購) 商用機器不代表低端機器在集群中(尤其是大的集群),節點失敗率是比較高的HDFS的目標是確保集群在節點失敗的時候不會讓用戶感覺到明顯的中斷。

2.2 HDFS不適合的應用類型

有些場景不適合使用HDFS來存儲數據。下面列舉幾個:

1) 低延時的數據訪問
對延時要求在毫秒級別的應用,不適合采用HDFS。HDFS是為高吞吐數據傳輸設計的,因此可能犧牲延時HBase更適合低延時的數據訪問。

2)大量小文件
文件的元數據(如目錄結構,文件block的節點列表,block-node mapping)保存在NameNode的內存中, 整個文件系統的文件數量會受限於NameNode的內存大小。
經驗而言,一個文件/目錄/文件塊一般占有150字節的元數據內存空間。如果有100萬個文件,每個文件占用1個文件塊,則需要大約300M的內存。因此十億級別的文件數量在現有商用機器上難以支持。

3)多方讀寫,需要任意的文件修改
HDFS采用追加(append-only)的方式寫入數據。不支持文件任意offset的修改。不支持多個寫入器(writer)。

3. HDFS核心概念

3.1 Blocks

物理磁盤中有塊的概念,磁盤的物理Block是磁盤操作最小的單元,讀寫操作均以Block為最小單元,一般為512 Byte。文件系統在物理Block之上抽象了另一層概念,文件系統Block物理磁盤Block的整數倍。通常為幾KB。Hadoop提供的df、fsck這類運維工具都是在文件系統的Block級別上進行操作。

HDFS的Block塊比一般單機文件系統大得多,默認為128M。HDFS的文件被拆分成block-sized的chunk,chunk作為獨立單元存儲。比Block小的文件不會占用整個Block,只會占據實際大小。例如, 如果一個文件大小為1M,則在HDFS中只會占用1M的空間,而不是128M。

HDFS的Block為什么這么大?
是為了最小化查找(seek)時間,控制定位文件與傳輸文件所用的時間比例。假設定位到Block所需的時間為10ms,磁盤傳輸速度為100M/s。如果要將定位到Block所用時間占傳輸時間的比例控制1%,則Block大小需要約100M。
但是如果Block設置過大,在MapReduce任務中,Map或者Reduce任務的個數 如果小於集群機器數量,會使得作業運行效率很低。

Block抽象的好處
block的拆分使得單個文件大小可以大於整個磁盤的容量,構成文件的Block可以分布在整個集群, 理論上,單個文件可以占據集群中所有機器的磁盤。
Block的抽象也簡化了存儲系統,對於Block,無需關注其權限,所有者等內容(這些內容都在文件級別上進行控制)。
Block作為容錯和高可用機制中的副本單元,即以Block為單位進行復制。

3.2 Namenode & Datanode

整個HDFS集群由Namenode和Datanode構成master-worker(主從)模式。Namenode復雜構建命名空間,管理文件的元數據等,而Datanode負責實際存儲數據,負責讀寫工作。

Namenode

Namenode存放文件系統樹及所有文件、目錄的元數據。元數據持久化為2種形式:

  • namespcae image-
  • edit log

但是持久化數據中不包括Block所在的節點列表,及文件的Block分布在集群中的哪些節點上,這些信息是在系統重啟的時候重新構建(通過Datanode匯報的Block信息)。
在HDFS中,Namenode可能成為集群的單點故障,Namenode不可用時,整個文件系統是不可用的。HDFS針對單點故障提供了2種解決機制:
1)備份持久化元數據
將文件系統的元數據同時寫到多個文件系統, 例如同時將元數據寫到本地文件系統及NFS。這些備份操作都是同步的、原子的。

2)Secondary Namenode
Secondary節點定期合並主Namenode的namespace image和edit log, 避免edit log過大,通過創建檢查點checkpoint來合並。它會維護一個合並后的namespace image副本, 可用於在Namenode完全崩潰時恢復數據。下圖為Secondary Namenode的管理界面:

這里寫圖片描述

Secondary Namenode通常運行在另一台機器,因為合並操作需要耗費大量的CPU和內存。其數據落后於Namenode,因此當Namenode完全崩潰時,會出現數據丟失。 通常做法是拷貝NFS中的備份元數據到Second,將其作為新的主Namenode。
在HA中可以運行一個Hot Standby,作為熱備份,在Active Namenode故障之后,替代原有Namenode成為Active Namenode。

Datanode

數據節點負責存儲和提取Block,讀寫請求可能來自namenode,也可能直接來自客戶端。數據節點周期性向Namenode匯報自己節點上所存儲的Block相關信息。

3.3 Block Caching

DataNode通常直接從磁盤讀取數據,但是頻繁使用的Block可以在內存中緩存。默認情況下,一個Block只有一個數據節點會緩存。但是可以針對每個文件可以個性化配置。
作業調度器可以利用緩存提升性能,例如MapReduce可以把任務運行在有Block緩存的節點上。
用戶或者應用可以向NameNode發送緩存指令(緩存哪個文件,緩存多久), 緩存池的概念用於管理一組緩存的權限和資源。

3.4 HDFS Federation

我們知道NameNode的內存會制約文件數量,HDFS Federation提供了一種橫向擴展NameNode的方式。在Federation模式中,每個NameNode管理命名空間的一部分,例如一個NameNode管理/user目錄下的文件, 另一個NameNode管理/share目錄下的文件。
每個NameNode管理一個namespace volumn,所有volumn構成文件系統的元數據。每個NameNode同時維護一個Block Pool,保存Block的節點映射等信息。各NameNode之間是獨立的,一個節點的失敗不會導致其他節點管理的文件不可用。
客戶端使用mount table將文件路徑映射到NameNode。mount table是在Namenode群組之上封裝了一層,這一層也是一個Hadoop文件系統的實現,通過viewfs:協議訪問。

3.5 HDFS HA

在HDFS集群中,NameNode依然是單點故障(SPOF)。元數據同時寫到多個文件系統以及Second NameNode定期checkpoint有利於保護數據丟失,但是並不能提高可用性。
這是因為NameNode是唯一一個對文件元數據和file-block映射負責的地方, 當它掛了之后,包括MapReduce在內的作業都無法進行讀寫。

當NameNode故障時,常規的做法是使用元數據備份重新啟動一個NameNode。元數據備份可能來源於:

  • 多文件系統寫入中的備份
  • Second NameNode的檢查點文件

啟動新的Namenode之后,需要重新配置客戶端和DataNode的NameNode信息。另外重啟耗時一般比較久,稍具規模的集群重啟經常需要幾十分鍾甚至數小時,造成重啟耗時的原因大致有:
1) 元數據鏡像文件載入到內存耗時較長。
2) 需要重放edit log
3) 需要收到來自DataNode的狀態報告並且滿足條件后才能離開安全模式提供寫服務。

Hadoop的HA方案

采用HA的HDFS集群配置兩個NameNode,分別處於Active和Standby狀態。當Active NameNode故障之后,Standby接過責任繼續提供服務,用戶沒有明顯的中斷感覺。一般耗時在幾十秒到數分鍾。
HA涉及到的主要實現邏輯有

1) 主備需共享edit log存儲。
主NameNode和待命的NameNode共享一份edit log,當主備切換時,Standby通過回放edit log同步數據。
共享存儲通常有2種選擇

  • NFS:傳統的網絡文件系統
  • QJM:quorum journal manager

QJM是專門為HDFS的HA實現而設計的,用來提供高可用的edit log。QJM運行一組journal node,edit log必須寫到大部分的journal nodes。通常使用3個節點,因此允許一個節點失敗,類似ZooKeeper。注意QJM沒有使用ZK,雖然HDFS HA的確使用了ZK來選舉主Namenode。一般推薦使用QJM。

2)DataNode需要同時往主備發送Block Report
因為Block映射數據存儲在內存中(不是在磁盤上),為了在Active NameNode掛掉之后,新的NameNode能夠快速啟動,不需要等待來自Datanode的Block Report,DataNode需要同時向主備兩個NameNode發送Block Report。

3)客戶端需要配置failover模式(對用戶透明)
Namenode的切換對客戶端來說是無感知的,通過客戶端庫來實現。客戶端在配置文件中使用的HDFS URI是邏輯路徑,映射到一對Namenode地址。客戶端會不斷嘗試每一個Namenode地址直到成功。

4)Standby替代Secondary NameNode
如果沒有啟用HA,HDFS獨立運行一個守護進程作為Secondary Namenode。定期checkpoint,合並鏡像文件和edit日志。

如果當主Namenode失敗時,備份Namenode正在關機(停止 Standby),運維人員依然可以從頭啟動備份Namenode,這樣比沒有HA的時候更省事,算是一種改進,因為重啟整個過程已經標准化到Hadoop內部,無需運維進行復雜的切換操作。

NameNode的切換通過代failover controller來實現。failover controller有多種實現,默認實現使用ZooKeeper來保證只有一個Namenode處於active狀態。

每個Namenode運行一個輕量級的failover controller進程,該進程使用簡單的心跳機制來監控Namenode的存活狀態並在Namenode失敗是觸發failover。Failover可以由運維手動觸發,例如在日常維護中需要切換主Namenode,這種情況graceful failover,非手動觸發的failover稱為ungraceful failover。

在ungraceful failover的情況下,沒有辦法確定失敗(被判定為失敗)的節點是否停止運行,也就是說觸發failover后,之前的主Namenode可能還在運行。QJM一次只允許一個Namenode寫edit log,但是之前的主Namenode仍然可以接受讀請求。Hadoop使用fencing來殺掉之前的Namenode。Fencing通過收回之前Namenode對共享的edit log的訪問權限、關閉其網絡端口使得原有的Namenode不能再繼續接受服務請求。使用STONITH技術也可以將之前的主Namenode關機。

最后,HA方案中Namenode的切換對客戶端來說是不可見的,前面已經介紹過,主要通過客戶端庫來完成。

4. 命令行接口

HDFS提供了各種交互方式,例如通過Java API、HTTP、shell命令行的。命令行的交互主要通過hadoop fs來操作。例如:

1、hadoop fs -copyFromLocal // 從本地復制文件到HDFS 2、hadoop fs mkdir // 創建目錄 3、hadoop fs -ls // 列出文件列表

Hadoop中,文件和目錄的權限類似於POSIX模型,包括讀、寫、執行3種權限:

  • 讀權限(r):用於讀取文件或者列出目錄中的內容
  • 寫權限(w):對於文件,就是文件的寫權限。目錄的寫權限指在該目錄下創建或者刪除文件(目錄)的權限。
  • 執行權限(x):文件沒有所謂的執行權限,被忽略。對於目錄,執行權限用於訪問器目錄下的內容。

每個文件或目錄都有owner,group,mode三個屬性,owner指文件的所有者,group為權限組。mode
由所有者權限、文件所屬的組中組員的權限、非所有者非組員的權限組成。下圖表示其所有者root擁有讀寫權限,supergroup組的組員有讀權限,其他人有讀權限。

這里寫圖片描述

文件權限是否開啟通過dfs.permissions.enabled屬性來控制,這個屬性默認為false,沒有打開安全限制,因此不會對客戶端做授權校驗,如果開啟安全限制,會對操作文件的用戶做權限校驗。特殊用戶superuser是Namenode進程的標識,不會針對該用戶做權限校驗。

最后看一下ls命令的執行結果:

這里寫圖片描述

這個返回結果類似於Unix系統下的ls命令,第一欄為文件的mode,d表示目錄,緊接着3種權限9位。 第二欄是指文件的副本數,這個數量通過dfs.replication配置,目錄則使用-表示沒有副本一說。其他諸如所有者、組、更新時間、文件大小跟Unix系統中的ls命令一致。

如果需要查看集群狀態或者瀏覽文件目錄,可以訪問Namenode暴露的Http Server查看集群信息,一般在namenode所在機器的50070端口。

這里寫圖片描述

這里寫圖片描述

這里寫圖片描述

5. Hadoop文件系統

前面Hadoop的文件系統概念是抽象的,HDFS只是其中的一種實現。Hadoop提供的實現如下圖:

這里寫圖片描述
這里寫圖片描述

簡單介紹一下,Local是對本地文件系統的抽象,hdfs就是我們最常見的,兩種web形式(webhdfs,swebhdfs)的實現通過HTTP提供文件操作接口。har是Hadoop體系下的壓縮文件,檔文件很多的時候可以壓縮成一個大文件,可以有效減少元數據的數量。viewfs就是我們前面介紹HDFS Federation張提到的,用來在客戶端屏蔽多個Namenode的底層細節。ftp顧名思義,就是使用ftp協議來實現,對文件的操作轉化為ftp協議。s3a是對Amazon雲服務提供的存儲系統的實現,azure則是微軟的雲服務平台實現。

前面我們提到了使用命令行跟HDFS交互,事實上還有很多方式來操作文件系統。例如Java應用程序可以使用org.apache.hadoop.fs.FileSystem來操作,其他形式的操作也都是基於FileSystem進行封裝。我們這里主要介紹一下HTTP的交互方式。
WebHDFS和SWebHDFS協議將文件系統暴露HTTP操作,這種交互方式比原生的Jav客戶端慢,不適合操作大文件。通過HTTP,有2種訪問方式,直接訪問和通過代理訪問

直接訪問
直接訪問的示意圖如下:

這里寫圖片描述

Namenode和Datanode默認打開了嵌入式web server,即dfs.webhdfs.enabled默認為true。webhdfs通過這些服務器來交互。元數據的操作通過namenode完成,文件的讀寫首先發到namenode,然后重定向到datanode讀取(寫入)實際的數據流。

通過HDFS代理

這里寫圖片描述

采用代理的示意圖如上所示。 使用代理的好處是可以通過代理實現負載均衡或者對帶寬進行限制,或者防火牆設置。代理通過HTTP或者HTTPS暴露為WebHDFS,對應為webhdfs和swebhdfs URL Schema。

代理作為獨立的守護進程,獨立於namenode和datanode,使用httpfs.sh腳本,默認運行在14000端口

除了FileSystem直接操作,命令行,HTTTP外,還有C語言API,NFS,FUSER等方式,這里不做過多介紹。

6. Java接口

實際的應用中,對HDFS的大多數操作還是通過FileSystem來操作,這部分重點介紹一下相關的接口,主要關注HDFS的實現類DistributedFileSystem及相關類。

6.1 讀操作

可以使用URL來讀取數據,或者而直接使用FileSystem操作。

從Hadoop URL讀取數據

java.net.URL類提供了資源定位的統一抽象,任何人都可以自己定義一種URL Schema,並提供相應的處理類來進行實際的操作。hdfs schema便是這樣的一種實現。

InputStream in = null; try { in = new URL("hdfs://master/user/hadoop").openStream(); }finally{ IOUtils.closeStream(in); }

為了使用自定義的Schema,需要設置URLStreamHandlerFactory,這個操作一個JVM只能進行一次,多次操作會導致不可用,通常在靜態塊中完成。下面的截圖是一個使用示例:

這里寫圖片描述

這里寫圖片描述

使用FileSystem API讀取數據

1) 首先獲取FileSystem實例,一般使用靜態get工廠方法

public static FileSystem get(Configuration conf) throws IOException public static FileSystem get(URI uri , Configuration conf) throws IOException public static FileSystem get(URI uri , Configuration conf,String user) throws IOException

如果是本地文件,通過getLocal獲取本地文件系統對象:

public static LocalFileSystem getLocal(COnfiguration conf) thrown IOException

2)調用FileSystem的open方法獲取一個輸入流:

public FSDataInputStream open(Path f) throws IOException public abstarct FSDataInputStream open(Path f , int bufferSize) throws IOException

默認情況下,open使用4KB的Buffer,可以根據需要自行設置。

3)使用FSDataInputStream進行數據操作
FSDataInputStream是java.io.DataInputStream的特殊實現,在其基礎上增加了隨機讀取、部分讀取的能力

public class FSDataInputStream extends DataInputStream implements Seekable, PositionedReadable, ByteBufferReadable, HasFileDescriptor, CanSetDropBehind, CanSetReadahead, HasEnhancedByteBufferAccess

隨機讀取操作通過Seekable接口定義:

public interface Seekable { void seek(long pos) throws IOException; long getPos() throws IOException; }

seek操作開銷昂貴,慎用。

部分讀取通過PositionedReadable接口定義:

public interface PositionedReadable{ public int read(long pistion ,byte[] buffer,int offser , int length) throws IOException; public int readFully(long pistion ,byte[] buffer,int offser , int length) throws IOException; public int readFully(long pistion ,byte[] buffer) throws IOException; }

6.2 寫數據

在HDFS中,文件使用FileSystem類的create方法及其重載形式來創建,create方法返回一個輸出流FSDataOutputStream,可以調用返回輸出流的getPos方法查看當前文件的位移,但是不能進行seek操作,HDFS僅支持追加操作。

創建時,可以傳遞一個回調接口Peofressable,獲取進度信息

append(Path f)方法用於追加內容到已有文件,但是並不是所有的實現都提供該方法,例如Amazon的文件實現就沒有提供追加功能。

下面是一個例子:

String localSrc =  args[0]; String dst = args[1]; InputStream in = new BufferedInputStream(new FileInputStream(localSrc)); COnfiguration conf = new Configuration(); FileSystem fs = FileSystem.get(URI.create(dst),conf); OutputStream out = fs.create(new Path(dst), new Progressable(){ public vid progress(){ System.out.print(.); } }); IOUtils.copyBytes(in , out, 4096,true);

6.3 目錄操作

使用mkdirs()方法,會自動創建沒有的上級目錄

HDFS中元數據封裝在FileStatus類中,包括長度、block size,replicaions,修改時間、所有者、權限等信息。使用FileSystem提供的getFileStatus方法獲取FileStatus。exists()方法判斷文件或者目錄是否存在;

列出文件(list),則使用listStatus方法,可以查看文件或者目錄的信息

  public abstract FileStatus[] listStatus(Path f) throws FileNotFoundException, IOException;

Path是個文件的時候,返回長度為1的數組。FileUtil提供的stat2Paths方法用於將FileStatus轉化為Path對象。

globStatus則使用通配符對文件路徑進行匹配:

public FileStatus[] globStatus(Path pathPattern) throws IOException

PathFilter用於自定義文件名過濾,不能根據文件屬性進行過濾,類似於java.io.FileFilter。例如下面這個例子排除到給定正則表達式的文件:

public interfacePathFilter{ boolean accept(Path path); }

6.4 刪除數據

使用FileSystem的delete()方法

public boolean delete(Path f , boolean recursive) throws IOException;

recursive參數在f是個文件的時候被忽略。如果f是文件並且recursice為true,則刪除整個目錄,否則拋出異常.

7. 數據流(讀寫流程)

接下來詳細介紹HDFS讀寫數據的流程,以及一致性模型相關的一些概念。

7.1 讀文件

大致讀文件的流程如下:

這里寫圖片描述

1)客戶端傳遞一個文件Path給FileSystem的open方法

2)DFS采用RPC遠程獲取文件最開始的幾個block的datanode地址。Namenode會根據網絡拓撲結構決定返回哪些節點(前提是節點有block副本),如果客戶端本身是Datanode並且節點上剛好有block副本,直接從本地讀取。

3)客戶端使用open方法返回的FSDataInputStream對象讀取數據(調用read方法)

4)DFSInputStream(FSDataInputStream實現了改類)連接持有第一個block的、最近的節點,反復調用read方法讀取數據

5)第一個block讀取完畢之后,尋找下一個block的最佳datanode,讀取數據。如果有必要,DFSInputStream會聯系Namenode獲取下一批Block 的節點信息(存放於內存,不持久化),這些尋址過程對客戶端都是不可見的。

6)數據讀取完畢,客戶端調用close方法關閉流對象

在讀數據過程中,如果與Datanode的通信發生錯誤,DFSInputStream對象會嘗試從下一個最佳節點讀取數據,並且記住該失敗節點, 后續Block的讀取不會再連接該節點
讀取一個Block之后,DFSInputStram會進行檢驗和驗證,如果Block損壞,嘗試從其他節點讀取數據,並且將損壞的block匯報給Namenode。
客戶端連接哪個datanode獲取數據,是由namenode來指導的,這樣可以支持大量並發的客戶端請求,namenode盡可能將流量均勻分布到整個集群。
Block的位置信息是存儲在namenode的內存中,因此相應位置請求非常高效,不會成為瓶頸。

7.2 寫文件

這里寫圖片描述

步驟分解
1)客戶端調用DistributedFileSystem的create方法

2)DistributedFileSystem遠程RPC調用Namenode在文件系統的命名空間中創建一個新文件,此時該文件沒有關聯到任何block。 這個過程中,Namenode會做很多校驗工作,例如是否已經存在同名文件,是否有權限,如果驗證通過,返回一個FSDataOutputStream對象。 如果驗證不通過,拋出異常到客戶端。

3)客戶端寫入數據的時候,DFSOutputStream分解為packets,並寫入到一個數據隊列中,該隊列由DataStreamer消費。

4)DateStreamer負責請求Namenode分配新的block存放的數據節點。這些節點存放同一個Block的副本,構成一個管道。 DataStreamer將packer寫入到管道的第一個節點,第一個節點存放好packer之后,轉發給下一個節點,下一個節點存放 之后繼續往下傳遞。

5)DFSOutputStream同時維護一個ack queue隊列,等待來自datanode確認消息。當管道上的所有datanode都確認之后,packer從ack隊列中移除。

6)數據寫入完畢,客戶端close輸出流。將所有的packet刷新到管道中,然后安心等待來自datanode的確認消息。全部得到確認之后告知Namenode文件是完整的。 Namenode此時已經知道文件的所有Block信息(因為DataStreamer是請求Namenode分配block的),只需等待達到最小副本數要求,然后返回成功信息給客戶端。

Namenode如何決定副本存在哪個Datanode?

HDFS的副本的存放策略是可靠性、寫帶寬、讀帶寬之間的權衡。默認策略如下:

  • 第一個副本放在客戶端相同的機器上,如果機器在集群之外,隨機選擇一個(但是會盡可能選擇容量不是太慢或者當前操作太繁忙的)
  • 第二個副本隨機放在不同於第一個副本的機架上。
  • 第三個副本放在跟第二個副本同一機架上,但是不同的節點上,滿足條件的節點中隨機選擇。
  • 更多的副本在整個集群上隨機選擇,雖然會盡量便面太多副本在同一機架上。
    副本的位置確定之后,在建立寫入管道的時候,會考慮網絡拓撲結構。下面是可能的一個存放策略:

這里寫圖片描述

這樣選擇很好滴平衡了可靠性、讀寫性能

  • 可靠性:Block分布在兩個機架上
  • 寫帶寬:寫入管道的過程只需要跨越一個交換機
  • 讀帶寬:可以從兩個機架中任選一個讀取

7.3 一致性模型

一致性模型描述文件系統中讀寫操縱的可見性。HDFS中,文件一旦創建之后,在文件系統的命名空間中可見:

Path p = new Path("p"); fs.create(p); assertTaht(fs.exists(p),is(true));

但是任何被寫入到文件的內容不保證可見,即使對象流已經被刷新。
“`java
Path p = new Path(“p”);
OutputStream out = fs.create(p);
out.write(“content”.getBytes(“UTF-8”));
out.flush();
assertTaht(fs.getFileStatus(p).getLen,0L); // 為0,即使調用了flush


如果需要強制刷新數據到Datanode,使用FSDataOutputStream的hflush方法強制將緩沖刷到datanode
hflush之后,HDFS保證到這個時間點為止寫入到文件的數據都到達所有的數據節點。
 ```java
Path p = new Path("p"); OutputStream out = fs.create(p); out.write("content".getBytes("UTF-8")); out.flush(); assertTaht(fs.getFileStatus(p).getLen,is(((long,"content".length())));

關閉對象流時,內部會調用hflush方法,但是hflush不保證datanode數據已經寫入到磁盤,只是保證寫入到datanode的內存, 因此在機器斷電的時候可能導致數據丟失,如果要保證寫入磁盤,使用hsync方法,hsync類型與fsync()的系統調用,fsync提交某個文件句柄的緩沖數據。

FileOutputStreamout = new FileOutPutStream(localFile); out.write("content".getBytes("UTF-8")); out.flush(); out.getFD().sync(); assertTaht(localFile.getLen,is(((long,"content".length())));

使用hflush或hsync會導致吞吐量下降,因此設計應用時,需要在吞吐量以及數據的健壯性之間做權衡。

另外,文件寫入過程中,當前正在寫入的Block對其他Reader不可見。

7.4 Hadoop節點距離

在讀取和寫入的過程中,namenode在分配Datanode的時候,會考慮節點之間的距離。HDFS中,距離沒有
采用帶寬來衡量,因為實際中很難准確度量兩台機器之間的帶寬。
Hadoop把機器之間的拓撲結構組織成樹結構,並且用到達公共父節點所需跳轉數之和作為距離。事實上這是一個距離矩陣的例子。下面的例子簡明地說明了距離的計算:

這里寫圖片描述

這里寫圖片描述

Hadoop集群的拓撲結構需要手動配置,如果沒配置,Hadoop默認所有節點位於同一個數據中心的同一機架上。

8 相關運維工具

8.1 使用distcp並行復制

前面的關注點都在於單線程的訪問,如果需要並行處理文件,需要自己編寫應用。Hadoop提供的distcp工具用於並行導入數據到Hadoop或者從Hadoop導出。一些例子:

hadoop distcp file1 file2  //可以作為fs -cp命令的高效替代 hadoop distcp dir1 dir2 hadoop distcp -update dir1 dir2 #update參數表示只同步被更新的文件,其他保持不變

distcp是底層使用MapReduce實現,只有map實現,沒有reduce。在map中並行復制文件。 distcp盡可能在map之間平均分配文件。map的數量可以通過-m參數指定:

hadoop distcp -update -delete -p hdfs://master1:9000/foo hdfs://master2/foo 

這樣的操作常用於在兩個集群之間復制數據,update參數表示只同步被更新過的數據,delete會刪除目標目錄中存在,但是源目錄不存在的文件。p參數表示保留文件的全校、block大小、副本數量等屬性。

如果兩個集群的Hadoop版本不兼容,可以使用webhdfs協議:

hadoop distcp webhdfs: //namenode1: 50070/foo webhdfs: //namenode2: 50070/foo

8.2 平衡HDFS集群

在distcp工具中,如果我們指定map數量為1,不僅速度很慢,每個Block第一個副本將全部落到運行這個唯一map的節點上,直到磁盤溢出。因此使用distcp的時候,最好使用默認的map數量,即20.
HDFS在Block均勻分布在各個節點上的時候工作得最好,如果沒有辦法在作業中盡量保持集群平衡,例如為了限制map數量(以便其他節點可以被別的作業使用),那么可以使用balancer工具來調整集群的Block分布。

參考

主要參考《Hadoop》權威指南第3章,自己進一步整理。感受原作者提供這么好的書籍。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM