HDFS分布式文件系統
1.目標
- 理解分布式思想
- 學會使用HDFS的常用命令
- 掌握如何使用java api操作HDFS
- 能獨立描述HDFS三大組件namenode、secondarynamenode、datanode的作用
- 理解并獨立描述HDFS讀寫流程
- HDFS如何解決大量小文件存儲問題
2. HDFS
2.1 HDFS是什么
-
HDFS是Hadoop中的一個存儲子模塊
-
HDFS (全稱Hadoop Distributed File System),即hadoop的分布式文件系統
-
File System文件系統:
-
操作系統中負責管理文件、存儲文件信息的軟件;
-
具體地說,它負責為用戶創建文件,存入、讀取、修改、轉儲、刪除文件等等操作
-
比如windows下的文件系統
-
-
分布式文件系統(distributed filesystem)
-
當數據集大小超出一臺計算機的存儲能力時,就有必要將它拆分成若干部分,然后分散到不同的計算機中存儲。
-
管理網絡中跨多臺計算機存儲的文件系統稱之為分布式文件系統
-
2.2 HDFS特點
2.2.1 優點:
- 適合存儲大文件,能用來存儲管理PB級的數據;不適合存儲小文件
- 存儲非結構化數據
- 流式的訪問數據,一次寫入、多次讀寫
- 運行于廉價的商用機器集群上,成本低
- 高容錯:故障時能繼續運行且不讓用戶察覺到明顯的中斷
- 容量可擴展
2.2.2 局限性
- 不適合處理低延遲數據訪問
- DFS是為了處理大型數據集分析任務的,主要是為達到高的數據吞吐量而設計的
- 對于低延時的訪問需求,HBase是更好的選擇
- 無法高效存儲大量的小文件
- 小文件會給Hadoop的擴展性和性能帶來嚴重問題(How?)
- 利用SequenceFile、MapFile等方式歸檔小文件(How?)
- 不支持多用戶寫入及任意修改文件
- 文件有一個寫入者,只能執行追加操作
- 不支持多個用戶對同一文件的寫操作,以及在文件任意位置進行修改,但支持追加
2.3 小結
- HDFS是Hadoop中的分布式文件系統
- HDFS高容錯
- 可擴展
- HDFS適合存儲大文件,不適合存儲小文件
- 不適合處理低延時的數據方問
3. HDFS初體驗
3.1 HDFS命令(20分鐘)
若熟悉基本的linux命令,HDFS學起來so easy
-
HDFS命令與linux 命令的相似性
-
參考課件《HDFS命令》
3.2 WEB UI界面
注意:
若在windows下,能夠訪問node01:50070,需要配置C:\Windows\System32\drivers\etc\hosts文件,末尾添加如下三行內容
192.168.51.100 node01
192.168.51.110 node02
192.168.51.120 node03
- 訪問HDFS的web界面,瀏覽器訪問
node01:50070
3.3 HDFS編程
-
HDFS java API編程
-
如何建MAVEN工程:①pom.xml文件;②建包、建類;
-
如何編寫HDFS讀寫代碼
-
如何運行代碼:
-
方式①本地運行(代碼右鍵->run);
-
方式②打包運行:
-
如何打包?兩種方式;
-
方式一(得獨立安裝maven):
mvn clean package -DskipTests # 在工程目錄下執行
-
方式二:利用IDEA打包
-
運行jar包
[hadoop@node01 ~]$ hadoop jar com.kaikeba.hadoop-1.0-SNAPSHOT.jar com.kaikeba.hadoop.hdfs.CopyFileFromLocal /kkb/install/hadoop-2.6.0-cdh5.14.2/README.txt /README.txt
-
-
-
如何查看官方API文檔
網址 -
HDFS代碼
向HDFS上傳文件
package com.kaikeba.hadoop.hdfs;import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.fs.FileSystem; import org.apache.hadoop.fs.Path; import org.apache.hadoop.io.IOUtils;import java.io.*; import java.net.URI;/*** 將本地文件系統的文件通過java-API寫入到HDFS文件*/ public class CopyFileFromLocal {/*** @param args* args0 windows本地磁盤文件C:/test.txt 或虛擬機本地磁盤文件/kkb/install/hadoop-2.6.0-cdh5.14.2/README.txt* args1 hdfs上文件hdfs://node01:8020/test.txt*/public static void main(String[] args){//本地磁盤路徑String source = args[0];//先確保/data目錄存在String destination = args[1];//HDFS的路徑InputStream in = null;try {in = new BufferedInputStream(new FileInputStream(source));//HDFS讀寫的配置文件Configuration conf = new Configuration();FileSystem fs = FileSystem.get(URI.create(destination),conf);//調用Filesystem的create方法返回的是FSDataOutputStream對象//該對象不允許在文件中定位,因為HDFS只允許一個已打開的文件順序寫入或追加OutputStream out = fs.create(new Path(destination));IOUtils.copyBytes(in, out, 4096, true);} catch (FileNotFoundException e) {System.out.println("exception");e.printStackTrace();} catch (IOException e) {System.out.println("exception1");e.printStackTrace();}} }
從HDFS下載文件
package com.kaikeba.hadoop.hdfs;import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.fs.FSDataInputStream; import org.apache.hadoop.fs.FileSystem; import org.apache.hadoop.fs.Path; import org.apache.hadoop.io.IOUtils;import java.io.BufferedOutputStream; import java.io.FileOutputStream; import java.io.IOException; import java.net.URI;/*** 從HDFS讀取文件* 打包運行jar包 [bruce@node01 Desktop]$ hadoop jar com.kaikeba.hadoop-1.0-SNAPSHOT.jar com.kaikeba.hadoop.hdfs.ReadFileFromHDFS*/ public class ReadFileFromHDFS {/*** @param args* args0 hdfs上文件hdfs://node01:8020/test.txt* args1 windows本地磁盤文件C:/01 HK/高級03班/test01.txt或虛擬機本地磁盤文件*/public static void main(String[] args) {try {//源文件String srcFile = args[0];Configuration conf = new Configuration();FileSystem fs = FileSystem.get(URI.create(srcFile),conf);FSDataInputStream hdfsInStream = fs.open(new Path(srcFile));//本地文件BufferedOutputStream outputStream = new BufferedOutputStream(new FileOutputStream(args[1]));IOUtils.copyBytes(hdfsInStream, outputStream, 4096, true);} catch (IOException e) {e.printStackTrace();}} }
3.4 小結
-
學習HDFS命令,學會借助help命令
-
根據HDFS與linux命令的相似性,舉一反三
-
HDFS API編程時,要學會查看官方API文檔(不管什么編程都要會查看官方API文檔啊~~~)
4. 核心概念block!!!!
4.1 數據塊block
4.1.1 HDFS block塊
-
向HDFS上傳文件,是按照128M為單位,切分成一個個block,分散的存儲在集群的不同數據節點datanode上
-
問:HDFS中一個44M大小的block塊會不會占據128M的空間?
- 不會
- 小于128M大小的塊不會占據128M空間,此例占據44M
-
問:這樣存儲有沒有問題?
- hadoop集群搭建在廉價的商用服務器上,所以服務器有出問題的幾率
4.2 block副本
-
回答上一個問題;因為HDFS是用普通的商用服務器搭建起來的;所以有節點出問題的可能性;
-
那么如果每個block只有一份的話,當block所在的節點宕機后,此block將無法訪問,進而導致文件無法完整讀取
-
為保正數據的可用及容錯,HDFS設計成每個block共有三份,即三個副本
-
如何設置副本數?
-
replication = 3
-
hdfs-site.xml
<property><name>dfs.replication</name><value>3</value> </property>
-
4.3 機架存儲策略
- 實際機房中,會有機架rack,每個機架上若干服務器
- 每個block有三個副本;以block1為例
- 第一副本:在本機器(rack1機架中的datanode1)的HDFS目錄下存儲block1的第一個副本。
- 第二副本:在不同Rack(如rack2)的某個DataNode(datanode4)上存儲block1的第二個副本。
- 第三副本:在datanode4所在機架rack2下,找一臺其它的datanode節點(如datanode5),存儲block1的第三個副本。
- 能有多副本:隨機節點
了解下服務器參數:https://item.jd.com/4564487.html
機架:https://item.jd.com/16829137698.html
4.4 block的一些操作
-
設置文件副本數,有什么用?
-
數據分塊存儲和副本的存放,是保證可靠性和高性能的關鍵
-
方式一:使用命令設置文件副本數;動態生效,不需要重啟hadoop集群
hadoop fs -setrep -R 4 /path
-
方式二:修改配置文件hdfs-site.xml,需要 重啟hadoop集群才能生效
<property><name>dfs.replication</name><value>4</value> </property>
-
-
HDFS提供了fsck命令,用于檢查HDFS上文件和目錄的健康狀態、獲取文件的block信息和位置信息
[hadoop@node01 ~]$ hdfs fsck
- 查看文件中損壞的塊
[hadoop@node01 ~]$ hdfs fsck /tmall-201412-1w.csv -list-corruptfileblocks
- 刪除損壞的文件
[hadoop@node01 ~]$ hdfs fsck /tmall-201412-1w.csv -delete
- 查看文件的塊基本信息
hdfs fsck /02-041-0029.mp4 -files -blocks -locations
4.5 小結
- HDFS上的文件分塊存儲
- 默認每個塊block有3個副本
- 考慮機架存儲策略
- 關于block的一些常用命令:hdfs fsck 這個有時候會用到清理一些損壞的文件
5. HDFS架構(重點 40分鐘)
- 大多數分布式大數據框架都是主從架構
- HDFS也是主從架構Master|Slave或稱為管理節點|工作節點
- 主叫NameNode,中文稱“名稱節點”
- 從叫DataNode,中文稱“數據節點”
5.1 NameNode
5.1.1 文件系統
- file system文件系統:操作系統中負責管理文件、存儲文件信息的軟件
- 具體地說,它負責為用戶創建文件,存入、讀取、修改、轉儲、刪除文件等
- 讀文件 =>>找到文件 =>> 在哪 + 叫啥?
- 元數據
- 關于文件或目錄的描述信息,如文件所在路徑、文件名稱、文件類型等等,這些信息稱為文件的元數據metadata
- 注意:元數據的概念在其他的大數據中也屢有提及
- 命名空間
- 文件系統中,為了便于管理存儲介質上的內容,給每個目錄、目錄中的文件、子目錄都起了名字,這樣形成的層級結構,稱之為命名空間
- 同一個目錄中,不能有同名的文件或目錄
- 用處:這樣通過目錄+文件名稱的方式能夠唯一的定位一個文件
5.1.2 HDFS-NameNode
- HDFS本質上也是文件系統filesystem,所以它也有元數據metadata;
- HDFS元數據metadata保存在NameNode內存中
- NameNode作用
- HDFS的主節點
- 負責管理文件系統的命名空間,將HDFS的元數據存儲在NameNode節點的內存中
- 負責響應客戶端對文件的讀寫請求
- HDFS元數據
- 文件目錄樹、所有的文件(目錄)名稱、文件屬性(生成時間、副本、權限)、每個文件的塊列表、每個block塊所在的datanode列表
-
每個文件、目錄、block占用大概150Byte字節的元數據;所以HDFS適合存儲大文件,不適合存儲小文件
-
HDFS元數據信息以兩種形式保存:①編輯日志edits log②命名空間鏡像文件fsimage
- edits log:
- HDFS編輯日志文件 ,保存客戶端對HDFS的所有更改記錄,如增、刪、重命名文件(目錄),這些操作會修改HDFS目錄樹;
- NameNode會在編輯日志edit日志中記錄下來;
- fsimage:
- HDFS元數據鏡像文件 ,即將namenode內存中的元數據落入磁盤生成的文件;
- 保存了文件系統目錄樹信息以及文件、塊、datanode的映射關系,如下圖
- edits log:
說明:
①為hdfs-site.xml中屬性dfs.namenode.edits.dir的值決定;用于namenode保存edits.log文件
②為hdfs-site.xml中屬性dfs.namenode.name.dir的值決定;用于namenode保存fsimage文件
5.2 DataNode
- DataNode數據節點的作用
- 存儲block以及block元數據到datanode本地磁盤;
- 此處的元數據包括數據塊的長度、塊數據的校驗和、時間戳
5.3 SecondaryNameNode
-
為什么引入SecondaryNameNode
-
為什么元數據存儲在NameNode在內存中?這樣做有什么問題?如何解決?
-
HDFS編輯日志文件 editlog:在NameNode節點中的編輯日志editlog中,記錄下來客戶端對HDFS的所有更改的記錄,如增、刪、重命名文件(目錄);每次更改對應一個事務,每個事務有一個事務編號;事務編號遞增
-
作用:一旦系統出故障,可以根據editlog恢復元數據;
-
-
但editlog日志大小會隨著時間變的越來越大,導致系統重啟,根據日志恢復元數據的時間會越來越長;
-
為了避免這種情況,引入檢查點機制checkpoint,命名空間鏡像fsimage就是HDFS元數據的持久性檢查點,即將內存中的元數據落磁盤生成的文件;
-
此時,namenode如果重啟,可以將磁盤中的fsimage文件讀入內容,將元數據恢復到某一個檢查點,然后再執行檢查點之后記錄的編輯日志editlog,最后完全恢復元數據。
-
但是依然,隨著時間的推移,editlog記錄的日志會變多,那么當namenode重啟,恢復元數據過程中,會花越來越長的時間執行editlog中的每一個日志;而在namenode元數據恢復期間,HDFS不可用。
-
為了解決此問題,引入secondarynamenode輔助namenode,用來合并fsimage及editlog
-
-
SecondaryNameNode定期做checkpoint檢查點操作
- 創建檢查點checkpoint的兩大條件:
-
SecondaryNameNode每隔1小時創建一個檢查點
-
另外,Secondary NameNode每1分鐘檢查一次,從上一檢查點開始,edits日志文件中是否已包括100萬個事務,如果是,也會創建檢查點
-
checkpoint相關屬性(hdfs-site.xml)
-
屬性 值 解釋 dfs.namenode.checkpoint.period 3600秒(即1小時) The number of seconds between two periodic checkpoints. dfs.namenode.checkpoint.txns 1000000 The Secondary NameNode or CheckpointNode will create a checkpoint of the namespace every ‘dfs.namenode.checkpoint.txns’ transactions, regardless of whether ‘dfs.namenode.checkpoint.period’ has expired. dfs.namenode.checkpoint.check.period 60(1分鐘) The SecondaryNameNode and CheckpointNode will poll the NameNode every ‘dfs.namenode.checkpoint.check.period’ seconds to query the number of uncheckpointed transactions. -
Secondary NameNode首先請求原NameNode進行edits的滾動,這樣新的編輯操作就能夠進入新的文件中
-
Secondary NameNode通過HTTP GET方式讀取原NameNode中的fsimage及edits
-
Secondary NameNode讀取fsimage到內存中,然后執行edits中的每個操作,并創建一個新的統一的fsimage文件,有ckpt后綴
-
Secondary NameNode通過HTTP PUT方式將新的fsimage發送到原NameNode
-
原NameNode用新的fsimage替換舊的fsimage,同時系統會更新fsimage文件到記錄檢查點的時間。
-
這個過程結束后,NameNode就有了最新的fsimage文件和更小的edits文件
- 創建檢查點checkpoint的兩大條件:
-
SecondaryNameNode一般部署在另外一臺節點上
- 因為它需要占用大量的CPU時間
- 并需要與namenode一樣多的內存,來執行合并操作
-
如何查看edits日志文件
hdfs oev -i edits_0000000000000000256-0000000000000000363 -o /home/hadoop/edit1.xml
-
如何查看fsimage文件
hdfs oiv -p XML -i fsimage_0000000000000092691 -o fsimage.xml
5.4 心跳機制
工作原理:
- NameNode啟動的時候,會開一個ipc server在那里
- DataNode啟動后向NameNode注冊,每隔3秒鐘向NameNode發送一個“心跳heartbeat”
- 心跳返回結果帶有NameNode給該DataNode的命令,如復制塊數據到另一DataNode,或刪除某個數據塊
- 如果超過10分鐘NameNode沒有收到某個DataNode 的心跳,則認為該DataNode節點不可用
- DataNode周期性(6小時)的向NameNode上報當前DataNode上的塊狀態報告BlockReport;塊狀態報告包含了一個該 Datanode上所有數據塊的列表
心跳的作用:
-
通過周期心跳,NameNode可以向DataNode返回指令
-
可以判斷DataNode是否在線
-
通過BlockReport,NameNode能夠知道各DataNode的存儲情況,如磁盤利用率、塊列表;跟負載均衡有關
-
hadoop集群剛開始啟動時,99.9%的block沒有達到最小副本數(dfs.namenode.replication.min默認值為1),集群處于安全模式,涉及BlockReport;
相關配置項
- hdfs-default.xml
屬性 | 值 | 解釋 |
---|---|---|
dfs.heartbeat.interval | 3 | Determines datanode heartbeat interval in seconds. 心跳間隔 |
dfs.blockreport.intervalMsec | 21600000 (6小時) | Determines block reporting interval in milliseconds. 上傳塊報告時間間隔 |
- 查看hdfs-default.xml默認配置文件
5.5 負載均衡 start-balancer
-
什么原因會有可能造成不均衡?
- 機器與機器之間磁盤利用率不平衡是HDFS集群非常容易出現的情況
- 尤其是在DataNode節點出現故障或在現有的集群上增添新的DataNode的時候
-
為什么需要均衡?
- 防止熱點出現,提升集群存儲資源利用率
- 從存儲與計算兩方面提高集群性能
-
如何手動負載均衡?下邊命令無需重啟hadoop
$HADOOP_HOME/sbin/start-balancer.sh -t 5% # 磁盤利用率最高的節點若比最少的節點,大于5%,觸發均衡
- 停止負載均衡
$HADOOP_HOME/sbin/stop-balancer.sh
5.6 小結
- NameNode負責存儲HDFS集群的元數據,存在內存中
- DataNode負責存儲block塊及塊的元數據
- SecondaryNameNode主要負責對HDFS元數據做checkpoint操作
- 集群的心跳機制,讓集群中各節點形成一個整體;主節點知道從節點的死活
- 節點的上下線,導致存儲的不均衡,可以手動觸發負載均衡
6. HDFS讀寫流程(重點 30分鐘)
6.1 數據寫流程
6.1.1 詳細流程
-
創建文件:
- HDFS客戶端向HDFS寫數據,先調用DistributedFileSystem.create()方法,在HDFS創建新的空文件
- RPC(ClientProtocol.create())遠程過程調用NameNode(NameNodeRpcServer)的create(),首先在HDFS目錄樹指定路徑添加新文件
- 然后將創建新文件的操作記錄在editslog中
- NameNode.create方法執行完后,DistributedFileSystem.create()返回FSDataOutputStream,它本質是封裝了一個DFSOutputStream對象
-
建立數據流管道:
- 客戶端調用DFSOutputStream.write()寫數據
- DFSOutputStream調用ClientProtocol.addBlock(),首先向NameNode申請一個空的數據塊
- addBlock()返回LocatedBlock對象,對象包含當前數據塊的所有datanode的位置信息
- 根據位置信息,建立數據流管道
-
向數據流管道pipeline中寫當前塊的數據:
- 客戶端向流管道中寫數據,先將數據寫入一個檢驗塊chunk中,大小512Byte,寫滿后,計算chunk的檢驗和checksum值(4Byte)
- 然后將chunk數據本身加上checksum,形成一個帶checksum值的chunk(516Byte)
- 保存到一個更大一些的結構packet數據包中,packet為64kB大小
-
packet寫滿后,先被寫入一個dataQueue隊列中
- packet被從隊列中取出,向pipeline中寫入,先寫入datanode1,再從datanoe1傳到datanode2,再從datanode2傳到datanode3中
-
一個packet數據取完后,后被放入到ackQueue中等待pipeline關于該packet的ack的反饋
- 每個packet都會有ack確認包,逆pipeline(dn3 -> dn2 -> dn1)傳回輸出流
-
若packet的ack是SUCCESS成功的,則從ackQueue中,將packet刪除;否則,將packet從ackQueue中取出,重新放入dataQueue,重新發送
- 如果當前塊寫完后,文件還有其它塊要寫,那么再調用addBlock方法(流程同上)
-
文件最后一個block塊數據寫完后,會再發送一個空的packet,表示當前block寫完了,然后關閉pipeline
- 所有塊寫完,close()關閉流
-
ClientProtocol.complete()通知namenode當前文件所有塊寫完了
6.1.2 容錯
- 在寫的過程中,pipeline中的datanode出現故障(如網絡不通),輸出流如何恢復
- 輸出流中ackQueue緩存的所有packet會被重新加入dataQueue
- 輸出流調用ClientProtocol.updateBlockForPipeline(),為block申請一個新的時間戳,namenode會記錄新時間戳
- 確保故障datanode即使恢復,但由于其上的block時間戳與namenode記錄的新的時間戳不一致,故障datanode上的block進而被刪除
- 故障的datanode從pipeline中刪除
- 輸出流調用ClientProtocol.getAdditionalDatanode()通知namenode分配新的datanode到數據流pipeline中,并使用新的時間戳建立pipeline
- 新添加到pipeline中的datanode,目前還沒有存儲這個新的block,HDFS客戶端通過DataTransferProtocol通知pipeline中的一個datanode復制這個block到新的datanode中
- pipeline重建后,輸出流調用ClientProtocol.updatePipeline(),更新namenode中的元數據
- 故障恢復完畢,完成后續的寫入流程
6.2 數據讀流程
6.2.1 基本流程
- 1、client端讀取HDFS文件,client調用文件系統對象DistributedFileSystem的open方法
- 2、返回FSDataInputStream對象(對DFSInputStream的包裝)
- 3、構造DFSInputStream對象時,調用namenode的getBlockLocations方法,獲得file的開始若干block(如blk1, blk2, blk3, blk4)的存儲datanode(以下簡稱dn)列表;針對每個block的dn列表,會根據網絡拓撲做排序,離client近的排在前;
- 4、調用DFSInputStream的read方法,先讀取blk1的數據,與client最近的datanode建立連接,讀取數據
- 5、讀取完后,關閉與dn建立的流
- 6、讀取下一個block,如blk2的數據(重復步驟4、5、6)
- 7、這一批block讀取完后,再讀取下一批block的數據(重復3、4、5、6、7)
- 8、完成文件數據讀取后,調用FSDataInputStream的close方法
6.2.2 容錯
-
情況一:讀取block過程中,client與datanode通信中斷
- client與存儲此block的第二個datandoe建立連接,讀取數據
- 記錄此有問題的datanode,不會再從它上讀取數據
-
情況二:client讀取block,發現block數據有問題
- client讀取block數據時,同時會讀取到block的校驗和,若client針對讀取過來的block數據,計算檢驗和,其值與讀取過來的校驗和不一樣,說明block數據損壞
- client從存儲此block副本的其它datanode上讀取block數據(也會計算校驗和)
- 同時,client會告知namenode此情況;
7. Hadoop HA高可用
7.1 HDFS高可用原理
- 對于HDFS ,NN存儲元數據在內存中,并負責管理文件系統的命名空間和客戶端對HDFS的讀寫請求。但是,如果只存在一個NN,一旦發生“單點故障”,會使整個系統失效。
- 雖然有個SNN,但是它并不是NN的熱備份
- 因為SNN無法提供“熱備份”功能,在NN故障時,無法立即切換到SNN對外提供服務,即HDFS處于停服狀態。
- HDFS2.x采用了HA(High Availability高可用)架構。
- 在HA集群中,可設置兩個NN,一個處于“活躍(Active)”狀態,另一個處于“待命(Standby)”狀態。
- 由zookeeper確保一主一備(講zookeeper時具體展開)
- 處于Active狀態的NN負責響應所有客戶端的請求,處于Standby狀態的NN作為熱備份節點,保證與active的NN的元數據同步
- Active節點發生故障時,zookeeper集群會發現此情況,通知Standby節點立即切換到活躍狀態對外提供服務
- 確保集群一直處于可用狀態
- 如何熱備份元數據:
- Standby NN是Active NN的“熱備份”,因此Active NN的狀態信息必須實時同步到StandbyNN。
- 可借助一個共享存儲系統來實現狀態同步,如NFS(NetworkFile System)、QJM(Quorum Journal Manager)或者Zookeeper。
- Active NN將更新數據寫入到共享存儲系統,Standby NN一直監聽該系統,一旦發現有新的數據寫入,就立即從公共存儲系統中讀取這些數據并加載到Standby NN自己內存中,從而保證元數據與Active NN狀態一致。
- 塊報告:
- NN保存了數據塊到實際存儲位置的映射信息,為了實現故障時的快速切換,必須保證StandbyNN中也包含最新的塊映射信息
- 因此需要給所有DN配置Active和Standby兩個NN的地址,把塊的位置和心跳信息同時發送到兩個NN上。
8. Hadoop聯邦
8.1 為什么需要聯邦
- 雖然HDFS HA解決了“單點故障”問題,但HDFS在擴展性、整體性能和隔離性方面仍有問題
- 系統擴展性方面,元數據存儲在NN內存中,受限于內存上限(每個文件、目錄、block占用約150字節)
- 整體性能方面,吞吐量受單個NN的影響
- 隔離性方面,一個程序可能會影響其他程序的運行,如果一個程序消耗過多資源會導致其他程序無法順利運行
- HDFS HA本質上還是單名稱節點
8.2 聯邦 (多個命名空間)
- HDFS聯邦可以解決以上三個問題
- HDFS聯邦中,設計了多個命名空間;每個命名空間有一個NN或一主一備兩個NN,使得HDFS的命名服務能夠水平擴展
- 這些NN分別進行各自命名空間namespace和塊的管理,相互獨立,不需要彼此協調
- 每個DN要向集群中所有的NN注冊,并周期性的向所有NN發送心跳信息和塊信息,報告自己的狀態
- HDFS聯邦每個相互獨立的NN對應一個獨立的命名空間
- 每一個命名空間管理屬于自己的一組塊,這些屬于同一命名空間的塊對應一個“塊池”的概念。
- 每個DN會為所有塊池提供塊的存儲,塊池中的各個塊實際上是存儲在不同DN中的
8.3 擴展
聯邦-官網
9. 文件壓縮
9.1 壓縮算法
-
文件壓縮好處:
- 減少數據所占用的磁盤空間
- 加快數據在磁盤、網絡上的IO
-
常用壓縮格式
壓縮格式 UNIX工具 算 法 文件擴展名 可分割 DEFLATE 無 DEFLATE .deflate No gzip gzip DEFLATE .gz No zip zip DEFLATE .zip YES bzip bzip2 bzip2 .bz2 YES LZO lzop LZO .lzo No Snappy 無 Snappy .snappy No -
Hadoop的壓縮實現類;均實現CompressionCodec接口
壓縮格式 對應的編碼/解碼器 DEFLATE org.apache.hadoop.io.compress.DefaultCodec gzip org.apache.hadoop.io.compress.GzipCodec bzip2 org.apache.hadoop.io.compress.BZip2Codec LZO com.hadoop.compression.lzo.LzopCodec Snappy org.apache.hadoop.io.compress.SnappyCodec -
查看集群是否支持本地壓縮(所有節點都要確認)
[hadoop@node01 ~]$ hadoop checknative
9.2 編程實踐
-
編程:上傳壓縮過的文件到HDFS
- 對CopyFileFromLocal代碼做修改,向文件壓縮后,再上傳到HDFS
- 代碼
package com.kaikeba.hadoop.compress;import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.IOUtils;
import org.apache.hadoop.io.compress.BZip2Codec;
import org.apache.hadoop.io.compress.CompressionCodec;
import org.apache.hadoop.io.compress.CompressionOutputStream;
import org.apache.hadoop.util.ReflectionUtils;import java.io.*;
import java.net.URI;/**** 將本地文件系統的文件通過java-API寫入到HDFS文件,并且寫入時使用壓縮*/
public class CopyFileFromLocal {/**** @param args 兩個參數 C:\test\01_018分鐘.mp4 hdfs://node01:8020/copyFromLocal/01_018分鐘.bz2* @throws ClassNotFoundException*/public static void main(String[] args) throws ClassNotFoundException {//壓縮相關//壓縮類//HDFS讀寫的配置文件Configuration conf = new Configuration();BZip2Codec codec = new BZip2Codec();codec.setConf(conf);String source = args[0]; //linux或windows中的文件路徑,demo存在一定數據String destination="hdfs://node01:8020/copyFromLocal/01_018分鐘.bz2";//HDFS的路徑InputStream in = null;try {in = new BufferedInputStream(new FileInputStream(source));FileSystem fs = FileSystem.get(URI.create(destination),conf);//調用Filesystem的create方法返回的是FSDataOutputStream對象//該對象不允許在文件中定位,因為HDFS只允許一個已打開的文件順序寫入或追加OutputStream out = fs.create(new Path(destination));//對輸出流的數據壓縮CompressionOutputStream compressedOut = codec.createOutputStream(out);//流拷貝IOUtils.copyBytes(in, compressedOut, 4096, true);} catch (FileNotFoundException e) {System.out.println("exception");e.printStackTrace();} catch (IOException e) {System.out.println("exception1");e.printStackTrace();}}
}
- 擴展閱讀
- 《Hadoop權威指南》 5.2章節 壓縮
- HDFS文件壓縮
10. 小文件治理
10.1 HDFS不適合存儲小文件
- NameNode存儲著文件系統的元數據,每個文件、目錄、塊大概有150字節的元數據;
- NN內存有限,因此HDFS存儲文件數量的也有上限,如果小文件過多則會造成NN的壓力過大
- 且HDFS能存儲的數據總量也會變小
10.2 HAR文件方案(10分鐘)
- 此方案本質啟動mr程序,所以需要啟動yarn
用法:hadoop archive -archiveName .har -p [-r ]*
# 創建archive文件;/testhar有兩個子目錄th1、th2;兩個子目錄中有若干文件
hadoop archive -archiveName test.har -p /testhar -r 3 th1 th2 /outhar # 原文件還存在,需手動刪除# 查看archive文件
hdfs dfs -ls -R har:///outhar/test.har# 解壓archive文件
# 方式一
hdfs dfs -cp har:///outhar/test.har/th1 hdfs:/unarchivef1 # 順序解壓
hadoop fs -ls /unarchivef1
# 方式二
hadoop distcp har:///outhar/test.har/th1 hdfs:/unarchivef2 # 并行解壓,效率高,啟動MR
10.3 Sequence Files方案 !!!
- SequenceFile文件,主要由一條條record記錄組成;
- 具體結構(如上圖):
- 一個SequenceFile首先有一個4字節的header(文件版本號)
- 接著是若干record記錄
- 每個record是鍵值對形式的;鍵值類型是可序列化類型,如IntWritable、Text
- 記錄間會隨機的插入一些同步點sync marker,用于方便定位到記錄邊界
- SequenceFile文件可以作為小文件的存儲容器;
- 每條record保存一個小文件的內容
- 小文件名作為當前record的鍵;
- 小文件的內容作為當前record的值;
- 如10000個100KB的小文件,可以編寫程序將這些文件放到一個SequenceFile文件。
- 一個SequenceFile是可分割的,所以MapReduce可將文件切分成塊,每一塊獨立操作。
- 不像HAR,SequenceFile支持壓縮。記錄的結構取決于是否啟動壓縮
- 支持兩類壓縮:
- 不壓縮NONE,如上圖
- 壓縮RECORD,如上圖
- 壓縮BLOCK,如下圖,①一次性壓縮多條記錄;②每一個新塊Block開始處都需要插入同步點
- 在大多數情況下,以block(注意:指的是SequenceFile中的block)為單位進行壓縮是最好的選擇
- 因為一個block包含多條記錄,利用record間的相似性進行壓縮,壓縮效率更高
- 把已有的數據轉存為SequenceFile比較慢。比起先寫小文件,再將小文件寫入SequenceFile,一個更好的選擇是直接將數據寫入一個SequenceFile文件,省去小文件作為中間媒介.
- 支持兩類壓縮:
- 向SequenceFile寫入數據
package com.kaikeba.hadoop.sequencefile;import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.IOUtils;
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.SequenceFile;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.io.compress.BZip2Codec;import java.io.IOException;
import java.net.URI;public class SequenceFileWriteNewVersion {//模擬數據源;數組中一個元素表示一個文件的內容private static final String[] DATA = {"The Apache Hadoop software library is a framework that allows for the distributed processing of large data sets across clusters of computers using simple programming models.","It is designed to scale up from single servers to thousands of machines, each offering local computation and storage.","Rather than rely on hardware to deliver high-availability, the library itself is designed to detect and handle failures at the application layer","o delivering a highly-available service on top of a cluster of computers, each of which may be prone to failures.","Hadoop Common: The common utilities that support the other Hadoop modules."};public static void main(String[] args) throws IOException {//輸出路徑:要生成的SequenceFile文件名String uri = "hdfs://node01:8020/writeSequenceFile";Configuration conf = new Configuration();FileSystem fs = FileSystem.get(URI.create(uri), conf);//向HDFS上的此SequenceFile文件寫數據Path path = new Path(uri);//因為SequenceFile每個record是鍵值對的//指定key類型IntWritable key = new IntWritable(); //key數字 -> int -> IntWritable//指定value類型Text value = new Text();//value -> String -> Text//創建向SequenceFile文件寫入數據時的一些選項//要寫入的SequenceFile的路徑SequenceFile.Writer.Option pathOption = SequenceFile.Writer.file(path);//record的key類型選項SequenceFile.Writer.Option keyOption = SequenceFile.Writer.keyClass(IntWritable.class);//record的value類型選項SequenceFile.Writer.Option valueOption = SequenceFile.Writer.valueClass(Text.class);//SequenceFile壓縮方式:NONE | RECORD | BLOCK三選一//方案一:RECORD、不指定壓縮算法
// SequenceFile.Writer.Option compressOption = SequenceFile.Writer.compression(SequenceFile.CompressionType.RECORD);
// SequenceFile.Writer writer = SequenceFile.createWriter(conf, pathOption, keyOption, valueOption, compressOption);//方案二:BLOCK、不指定壓縮算法
// SequenceFile.Writer.Option compressOption = SequenceFile.Writer.compression(SequenceFile.CompressionType.BLOCK);
// SequenceFile.Writer writer = SequenceFile.createWriter(conf, pathOption, keyOption, valueOption, compressOption);//方案三:使用BLOCK、壓縮算法BZip2Codec;壓縮耗時間//再加壓縮算法BZip2Codec codec = new BZip2Codec();codec.setConf(conf);SequenceFile.Writer.Option compressAlgorithm = SequenceFile.Writer.compression(SequenceFile.CompressionType.RECORD, codec);//創建寫數據的Writer實例SequenceFile.Writer writer = SequenceFile.createWriter(conf, pathOption, keyOption, valueOption, compressAlgorithm);for (int i = 0; i < 100000; i++) {//分別設置key、value值key.set(100000 - i);value.set(DATA[i % DATA.length]); //%取模 3 % 3 = 0;System.out.printf("[%s]\t%s\t%s\n", writer.getLength(), key, value);//在SequenceFile末尾追加內容writer.append(key, value);}//關閉流IOUtils.closeStream(writer);}
}
- 命令查看SequenceFile內容
hadoop fs -text /writeSequenceFile
- 讀取SequenceFile文件
package com.kaikeba.hadoop.sequencefile;import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.IOUtils;
import org.apache.hadoop.io.SequenceFile;
import org.apache.hadoop.io.Writable;
import org.apache.hadoop.util.ReflectionUtils;import java.io.IOException;public class SequenceFileReadNewVersion {public static void main(String[] args) throws IOException {//要讀的SequenceFileString uri = "hdfs://node01:8020/writeSequenceFile";Configuration conf = new Configuration();Path path = new Path(uri);//Reader對象SequenceFile.Reader reader = null;try {//讀取SequenceFile的Reader的路徑選項SequenceFile.Reader.Option pathOption = SequenceFile.Reader.file(path);//實例化Reader對象reader = new SequenceFile.Reader(conf, pathOption);//根據反射,求出key類型對象Writable key = (Writable)ReflectionUtils.newInstance(reader.getKeyClass(), conf);//根據反射,求出value類型對象Writable value = (Writable)ReflectionUtils.newInstance(reader.getValueClass(), conf);long position = reader.getPosition();System.out.println(position);while (reader.next(key, value)) {String syncSeen = reader.syncSeen() ? "*" : "";System.out.printf("[%s%s]\t%s\t%s\n", position, syncSeen, key, value);//移動到下一個record開頭的位置position = reader.getPosition(); // beginning of next record}} finally {IOUtils.closeStream(reader);}}
}
11. 文件快照(10分鐘)
11.1 什么是快照
- 快照比較常見的應用場景是數據備份,以防一些用戶錯誤或災難恢復
- 快照snapshots是HDFS文件系統的,只讀的、某時間點的拷貝
- 可以針對某個目錄,或者整個文件系統做快照
- 創建快照時,block塊并不會被拷貝。快照文件中只是記錄了block列表和文件大小,不會做任何數據拷貝
11.2 快照操作
-
允許快照
允許一個快照目錄被創建。如果這個操作成功完成,這個目錄就變成snapshottable
用法:hdfs dfsadmin -allowSnapshot
hdfs dfsadmin -allowSnapshot /wordcount
-
禁用快照
用法:hdfs dfsadmin -disallowSnapshot
hdfs dfsadmin -disallowSnapshot /wordcount
-
創建快照(snapshotDir必須是snapshottable)
用法:hdfs dfs -createSnapshot []
#注意:先將/wordcount目錄變成允許快照的 hdfs dfs -createSnapshot /wordcount wcSnapshot
-
查看快照
hdfs dfs -ls /wordcount/.snapshot
-
重命名快照
這個操作需要擁有snapshottabl目錄所有者權限
用法:hdfs dfs -renameSnapshot
hdfs dfs -renameSnapshot /wordcount wcSnapshot newWCSnapshot
-
用快照恢復誤刪除數據
HFDS的/wordcount目錄,文件列表如下
誤刪除/wordcount/edit.xml文件
hadoop fs -rm /wordcount/edit.xml
恢復數據
hadoop fs -cp /wordcount/.snapshot/newWCSnapshot/edit.xml /wordcount
-
刪除快照
這個操作需要擁有snapshottabl目錄所有者權限
用法:hdfs dfs -deleteSnapshot
hdfs dfs -deleteSnapshot /wordcount newWCSnapshot
拓展總結
-
HDFS存儲地位
-
block塊為什么設置的比較大(面試)
- 磁盤基礎知識
- 盤片platter、磁頭head、磁道track、扇區sector、柱面cylinder
- 為了最小化尋址開銷;從磁盤傳輸數據的時間明顯大于定位這個塊開始位置所需的時間
- 問:塊的大小是不是設置的越大越好呢?
-
擴展閱讀:《HDFS新特性》
-
參考書籍:《Hadoop權威指南 第4版》
-
總結: