1. 程式人生 > >大資料學習-Hadoop生態章---(一)HDFS

大資料學習-Hadoop生態章---(一)HDFS

大資料學習-Hadoop生態章


(一) HDFS

1.1.Hadoop簡介

Hadoop是分散式的系統架構,是Apache基金會頂級金牌專案。
在這裡插入圖片描述
Hadoop實現了一個分散式檔案系統(Hadoop Distributed File System),簡稱HDFS

Hadoop的思想之源:

來自於Google 03年釋出3大論文, GFS、mapreduce、 Bigtable ;Dougcutting用Java實現)

Hadoop創始人介紹:

Hadoop作者Doug cutting,就職Yahoo期間開發了Hadoop專案,目前在Cloudera 公司從事架構工作。
2003-2004年,Google公開了部分GFS和Mapreduce思想的細節,以此為基礎Doug Cutting等人用了2年業餘時間實現了DFS和Mapreduce機制,一個微縮版:Nutch
Hadoop 於 2005 年秋天作為 Lucene的子專案 Nutch的一部分正式引入Apache基金會。
2006 年 3 月份,Map-Reduce分散式離線計算 和 Nutch Distributed File System (NDFS) nutch分散式檔案系統分別被納入稱為 Hadoop 的專案中。
(hadoop如今是Apache基金會頂級金牌專案)
Hadoop這名字來源於Doug Cutting兒子的玩具大象。

1.2.分散式檔案儲存系統—HDFS

1.2.1、HDFS是什麼?

分散式儲存系統HDFS (Hadoop Distributed File System)。
主要解決大資料的儲存問題。
分散式:
將一龐大的資料或一個複雜的業務,分發到不同的計算機節點和伺服器上面經行執行和處理。

1.2.2、HDFS的優缺點(重要)

優點:
(1) 分散式的特性

  • 適合大資料處理:GB 、TB 、甚至PB 級及以上的資料
  • 百萬規模以上的檔案數量:10K+ 節點。
  • 適合批處理:移動計算而非資料(MR),資料位置暴露給計算框架

(2) 自身特性:

  • 可構建在廉價機器上
  • 高可靠性:通過多副本提提高
  • 高容錯性:資料自動儲存多個副本;副本丟失後,自動恢復,提供了恢復機制

缺點:

  • 低延遲、高資料 吞吐訪問問題

    比如不支援毫秒級
    吞吐量大但有限制於其延遲

  • 小檔案存取佔用NameNode大量記憶體(尋道時間超過讀取時間(99%))
  • 不支援檔案修改:一個檔案只能有一個寫者(深入)
  • 僅支援append不支援修改(其實本身是支援的,主要為了空間換時間,節約成本)

1.3.HDFS架構圖(重點)

在這裡插入圖片描述

1.4.HDFS的功能模組及原理詳解(非常重要)

1.4.1.HDFS 資料儲存模型(block)

  1. 檔案被線性切分成固定大小的資料塊block

通過偏移量offset(單位:byte)標記;
預設資料塊大小為64MB (hadoop1.x版本,hadoop2.x以上版本預設128MB),可自定義配置;
若檔案大小不到64MB/128MB ,則單獨存成一個block

  1. 一個檔案儲存方式

按大小被切分成若干個block ,儲存到不同節點上;
預設情況下每個block都有2個副本 共3個副本;
副本數不大於節點數

  1. Block大小和副本數通過Client端上傳檔案時設定,檔案上傳成功後副本數可以變更,Block Size大小不可變更
    在這裡插入圖片描述在這裡插入圖片描述

1.4.2.NameNode(簡稱NN)

  1. NameNode主要功能
    - 接受客戶端的讀/寫服務。
    - 接受DN彙報的block位置資訊。
  2. NameNode儲存metadate(資料)元資訊
    - 基於記憶體儲存 :不會和磁碟發生 交換;
  3. metadate元資料資訊包括以下:
    -檔案owership(歸屬)和permissions(許可權)
    • 檔案大小 時間
    • Block列表[偏移量]:即一個完整檔案有哪些block(b0+b1+b2+…=file)
    • 位置資訊=Block每個副本儲存在哪個DataNode中(由DataNode啟動時上報 給NN 因為會隨時變化,不儲存在磁碟)–動態的!
  4. NameNode的metadate資訊在啟動後會載入到記憶體.。
    - metadata儲存到磁碟檔名為”fsimage”的映象檔案
    - Block的位置資訊不會儲存到fsimage
    - edits記錄對metadata的操作日誌

1.4.3.SecondaryNameNode(SNN)

  1. 幫助NN合併edits log檔案記憶體元資料資訊,落到磁碟形成fsimage映象檔案,減少NN啟動時間,它不是NN的備份(但可以做備份),保證資料安全。
  2. SNN執行合併時間和機制
    - 根據配置檔案設定的時間間隔fs.checkpoint.period 預設3600秒
    - 根據配置檔案設定edits log大小 fs.checkpoint.size 規定edits檔案的最大值預設是64MB

1.4.4.SecondaryNameNode SNN合併流程

1.從NN拷貝edits 檔案 和 fsimage映象檔案 ,edits變為一個新的edits.new檔案
2.將兩檔案資訊合併成檔案fsimage.ckpt,並傳回NN。
3.傳回檔案覆蓋原來fsimage檔案,edits.new變為edits檔案
在這裡插入圖片描述

1.4.5.DataNode(DN)

  1. 儲存資料(Block)
  2. 啟動DN執行緒的時候會向NameNode彙報block位置資訊
  3. 通過向NN傳送心跳保持與其聯絡(3秒一次),如果NN
    10分鐘沒有收到DN的心跳,則認為其已經lost,並copy其上的block到其它DN

1.4.6.Block的副本放置策略

  1. 第一個副本:叢集內部提交放置在上傳檔案的DN;如果是叢集外提交,則隨機挑選一臺磁碟不太滿,CPU不太忙的節點。(掌控資源詳情)
  2. 第二個副本:放置在於第一個副本不同的機架的節點上。
  3. 第三個副本:與第二個副本相同機架的不同節點。
  4. 更多副本:隨機節點
    在這裡插入圖片描述

1.4.7.HDFS讀寫流程

  • 讀檔案過程

在這裡插入圖片描述

  1. 首先呼叫FileSystem物件的open方法,其實是一個DistributedFileSystem的例項。
  2. DistributedFileSystem通過rpc協議獲得檔案的第一批block的locations地址,(同一個block按照重複數會返回多個locations,因為同一檔案的block分散式儲存在不同節點上),這些locations按照hadoop拓撲結構排序,距離客戶端近的排在前面(就近原則選擇)。
  3. 前兩步會返回一個FSDataInputStream物件,該物件會被封裝DFSInputStream物件,DFSInputStream可以方便的管理datanode和namenode資料流。客戶端呼叫read方法,DFSInputStream會找出離客戶端最近的datanode並連線
  4. 資料從datanode源源不斷的流向客戶端。
    這些操作對客戶端來說是透明的,客戶端的角度看來只是讀一個持續不斷的流。

注:

如果第一批block都讀完了, DFSInputStream就會去namenode拿下一批block的locations,然後繼續讀,如果所有的塊都讀完,這時就會關閉掉所有的流。

如果在讀資料的時候, DFSInputStream和datanode的通訊發生異常,就會嘗試正在讀的block的排序第二近的datanode,並且會記錄哪個datanode發生錯誤,剩餘的blocks讀的時候就會直接跳過該datanode。 DFSInputStream也會檢查block資料校驗和,如果發現一個壞的block,就會先報告到namenode節點,然後DFSInputStream在其他的datanode上讀該block的映象。
該設計就是客戶端直接連線datanode來檢索資料並且namenode來負責為每一個block提供最優的datanode, namenode僅僅處理block location的請求,這些資訊都載入在namenode的記憶體中,hdfs通過datanode叢集可以承受大量客戶端的併發訪問。

拓展瞭解:

RPC(Remote Procedure Call Protocol)——遠端過程呼叫協議,它是一種通過網路從遠端計算機程式上請求服務,而不需要了解底層網路技術的協議。**RPC協議**假定某些傳輸協議的存在,如TCP或UDP,為通訊程式之間攜帶資訊資料。在OSI網路通訊模型中,RPC跨越了傳輸層應用層。RPC使得開發包括網路分散式多程式在內的應用程式更加容易。

RPC採用客戶機/伺服器模式。請求程式就是一個客戶機,而服務提供程式就是一個伺服器。首先,客戶機呼叫程序傳送一個有程序引數的呼叫資訊到服務程序,然後等待應答資訊。在伺服器端,程序保持睡眠狀態直到呼叫資訊到達為止。當一個呼叫資訊到達,伺服器獲得程序引數,計算結果,傳送答覆資訊,然後等待下一個呼叫資訊,最後,客戶端呼叫程序接收答覆資訊,獲得程序結果,然後呼叫執行繼續進行。

  1. 寫檔案流程
    參考:http://shiyanjun.cn/archives/942.html
    參考:https://www.jianshu.com/p/1992b676eced
    在這裡插入圖片描述
    .
  2. 客戶端通過呼叫DistributedFileSystemcreate方法建立新檔案。
  3. DistributedFileSystem通過RPC呼叫namenode去建立一個沒有blocks關聯的新檔案,建立前, namenode會做各種校驗,比如檔案是否存在,客戶端有無許可權去建立等。如果校驗通過, namenode就會記錄下新檔案,否則就會丟擲IO異常。
  4. 前兩步結束後,會返回FSDataOutputStream的物件,與讀檔案的時候相似,FSDataOutputStream被封裝成DFSOutputStreamDFSOutputStream可以協調namenode和datanode。客戶端開始寫資料到DFSOutputStreamDFSOutputStream會把資料切成一個個小的packet,然後排成佇列data quene
  5. DataStreamer會去處理接受data quene,它先詢問namenode這個新的block最適合儲存的在哪幾個datanode裡(比如重複數是3,那麼就找到3個最適合的datanode),把他們排成一個管道pipeline輸出。DataStreamer把packet按佇列輸出到管道的第一個datanode中,第一個datanode又把packet輸出到第二個datanode中,以此類推。
  6. DFSOutputStream還有一個對列叫ack quene,也是由packet組成等待datanode的收到響應,當pipeline中的datanode都表示已經收到資料的時候,這時ack quene才會把對應的packet包移除掉。 如果在寫的過程中某個datanode發生錯誤,會採取以下幾步:
  1. pipeline被關閉掉;
  2. 為了防止防止丟包。ack quene裡的packet會同步到data quene裡;
  3. 建立新的pipeline管道懟到其他正常DN上
  4. 剩下的部分被寫到剩下的兩個正常的datanode中;
  5. namenode找到另外的datanode去建立這個塊的複製。當然,這些操作對客戶端來說是無感知的。
  1. 客戶端完成寫資料後呼叫close方法關閉寫入流。
  • 深入DFSOutputStream內部原理

開啟一個DFSOutputStream流,Client會寫資料到流內部的一個緩衝區中,然後資料被分解成多個Packet,每個Packet大小為64k位元組,每個Packet又由一組chunk和這組chunk對應的checksum資料組成,預設chunk大小為512位元組,每個checksum是對512位元組資料計算的校驗和資料。

===》當Client寫入的位元組流資料達到一個Packet的長度,這個Packet會被構建出來,然後會被放到佇列dataQueue中,接著DataStreamer執行緒會不斷地從dataQueue佇列中取出Packet,傳送到複製Pipeline中的第一個DataNode上,並將該Packet從dataQueue佇列中移到ackQueue佇列中。ResponseProcessor執行緒接收從Datanode傳送過來的ack,如果是一個成功的ack,表示複製Pipeline中的所有Datanode都已經接收到這個Packet,ResponseProcessor執行緒將packet從佇列ackQueue中刪除

====》 在傳送過程中,如果發生錯誤,錯誤的資料節點會被移除掉,ackqueue資料塊同步dataqueue中,然後重新建立一個新的Pipeline,排除掉出錯的那些DataNode節點,接著DataStreamer執行緒繼續從dataQueue佇列中傳送Packet。

下面是DFSOutputStream的結構及其原理,如圖所示:
在這裡插入圖片描述
注意:

客戶端執行write操作後,寫完的block才是可見的,正在寫的block對客戶端是不可見的,只有呼叫sync方法,客戶端才確保該檔案的寫操作已經全部完成,當客戶端呼叫close方法時,會預設呼叫sync方法。是否需要手動呼叫取決你根據程式需要在資料健壯性和吞吐率之間的權衡。

1.5.HDFS檔案許可權和安全模式

1.5.1.HDFS檔案許可權

  • 與Linux檔案許可權類似

r: read; w:write; x:execute,許可權x對於檔案忽略,對於資料夾表示是否允許訪問其內容

  • 如果Linux系統使用者zhangsan使用hadoop命令建立一個檔案,那麼這個 檔案在HDFS中owner就是zhangsan。
  • HDFS的許可權目的:阻止好人做錯事,而不是阻止壞人做壞事。HDFS 相信,你告訴我你是誰,我就認為你是誰。

1.5.2.安全模式

  • namenode啟動的時候,首先將映像檔案(fsimage)載入記憶體,並執行編輯日誌(edits)中的各 項操作。
  • 一旦在記憶體中成功建立檔案系統元資料的對映,則建立一個新的fsimage檔案(這個操作不需要SecondaryNameNode)和一個空的編輯日誌。
  • 此刻namenode執行在安全模式。即namenode的檔案系統對於客服端來說是隻讀的。(顯示 目錄,顯示檔案內容等。寫、刪除、重新命名都會失敗)。
  • 在此階段Namenode收集各個datanode的報告,當資料塊達到最小副本數以上時,會被認為是“安全”的, 在一定比例(可設定)的資料塊被確定為“安全”後,再過若干時間,安全模式結束
  • 當檢測到副本數不足的資料塊時,該塊會被複制直到達到最小副本數,系統中資料塊的位 置並不是由namenode維護的,而是以塊列表形式儲存在datanode中。

在這裡插入圖片描述

1.5.完全分散式搭建及eclipse外掛

參考 :完全分散式搭建及eclipse外掛

1.6.HDFS命令:

(1)檢視幫助
    hdfs dfs -help 
(2)檢視當前目錄資訊
    hdfs dfs -ls /
(3)上傳檔案
    hdfs dfs -put /本地路徑 /hdfs路徑
(4)剪下檔案
    hdfs dfs -moveFromLocal a.txt /aa.txt
(5)下載檔案到本地
    hdfs dfs -get /hdfs路徑 /本地路徑
(6)合併下載
    hdfs dfs -getmerge /hdfs路徑資料夾 /合併後的檔案
(7)建立資料夾
    hdfs dfs -mkdir /hello
(8)建立多級資料夾
    hdfs dfs -mkdir -p /hello/world
(9)移動hdfs檔案
    hdfs dfs -mv /hdfs路徑 /hdfs路徑
(10)複製hdfs檔案
    hdfs dfs -cp /hdfs路徑 /hdfs路徑
(11)刪除hdfs檔案
    hdfs dfs -rm /aa.txt
(12)刪除hdfs資料夾
    hdfs dfs -rm -r /hello
(13)檢視hdfs中的檔案
    hdfs dfs -cat /檔案
    hdfs dfs -tail -f /檔案
(14)檢視資料夾中有多少個檔案
    hdfs dfs -count /資料夾
(15)檢視hdfs的總空間
    hdfs dfs -df /
    hdfs dfs -df -h /
(16)修改副本數    
    hdfs dfs -setrep 1 /a.txt

1.7.My網盤

新建Java專案,匯入所需要的jar包
hadoop中的share\hadoop\hdfs
hadoop中的share\hadoop\hdfs\lib
hadoop中的share\hadoop\common
hadoop中的share\hadoop\common\lib
下的jar包。

block底層—offset偏移量來讀取位元組陣列
配置:
在這裡插入圖片描述
在這裡插入圖片描述
這個目錄下的2個配置檔案,拷貝到這裡
程式碼實現:

package yong.feng.qiaoke;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FSDataInputStream;
import org.apache.hadoop.fs.FileStatus;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.IOUtils;
public class HadoopDemo {
	private static Configuration cf;
	private static FileSystem fs;
	public static void main(String[] args) throws IOException {
		cf = new Configuration();
		fs = FileSystem.get(cf);
		// 建立、刪除 檔案
		// mk_del_dir();
		// 上傳檔案
		// uploadFile();
		// 下載
		// downFile();
		// 查詢
		showList();
	}
	private static void showList() throws FileNotFoundException, IOException {
		Path path = new Path("/");
		FileStatus[] filestatus = fs.listStatus(path);
		for (int i = 0; i < filestatus.length; i++) {
			FileStatus f = filestatus[i];
			System.out.println(f.getAccessTime() + "/t" + f.getModificationTime() + "/t" + f.getOwner() + "/t"
					+ f.getPath() + "/t" + f.getBlockSize() / 1024 / 1024 + "MB/t");
		}
	}
	private static void downFile() throws IOException {
		Path path = new Path("/ceshi/img0.jpg");
		File file = new File("C:/Users/hand/Desktop/img0.jpg");
		FSDataInputStream in = fs.open(path);
		FileOutputStream out = new FileOutputStream(file);
		IOUtils.copyBytes(in, out, cf);
	}
	private static void uploadFile() throws FileNotFoundException, IOException {
		Path path = new Path("/ceshi/img0.jpg");
		File file = new File("C:/Windows/Web/Wallpaper/Windows/img0.jpg");
		IOUtils.copyBytes(new FileInputStream(file), fs.create(path), cf);
		System.out.println("上傳成功");
	}
	private static void mk_del_dir() throws IOException {
		Path path = new Path("/ceshi");
		if (fs.exists(path)) {
			fs.delete(path, true);
			System.out.println("檔案存在");
		}
		fs.mkdirs(path);
		System.out.println("建立成功!1");
	}
}