一.block
HDFS 作爲一種文件系統,當然也需要有‘block’的概念。不過HDFS的block一般比較大,默認爲128MB。與普通的管理單個磁盤的文件系統一樣,HDFS也將文件分割成block,每個block都作爲一個獨立的單元分別保存。不同點在於,在HDFS中,小於block的文件不會佔用一個block的空間。(比如,文件大小爲1MB,那麼它會佔用一個HDFS的block,但是隻使用底層磁盤1MB的空間,而不是128MB。)
block爲什麼設置成128M:
如果塊設置過大
-
從磁盤傳輸數據的時間會明顯大於尋址時間,導致程序在處理這塊數據時,變得非常慢;
-
mapreduce中的map任務通常一次只處理一個塊中的數據,如果塊過大運行速度也會很慢。
如果塊設置過小
-
存放大量小文件會佔用NameNode中大量內存來存儲元數據,而NameNode的內存是有限的,不可取;
-
文件塊過小,尋址時間增大,導致程序一直在找block的開始位置。
因而,塊適當設置大一些,減少尋址時間,傳輸一個由多個塊組成的文件的時間主要取決於磁盤的傳輸速率。
爲什麼默認是128M
-
HDFS中平均尋址時間大概爲10ms;
-
經過前人的大量測試發現,尋址時間爲傳輸時間的1%時,爲最佳狀態;
所以最佳傳輸時間爲10ms/0.01=1000ms=1s -
目前磁盤的傳輸速率普遍爲100MB/s;
計算出最佳block大小:100MB/s x 1s = 100MB
所以我們設定block大小爲128MB。
實際在工業生產中, 磁盤傳輸速率爲200MB/s時,一般設定block大小爲256MB 磁盤傳輸速率爲400MB/s時,一般設定block大小爲512MB
二.HDFS 架構體系
HDFS 採用Master/Slave的架構來存儲數據,這種架構主要由四個部分組成,分別爲HDFS Client、NameNode、DataNode和Secondary NameNode。一個HDFS集羣是由一個NameNode和一定數目的DataNode組成的。NameNode是一箇中心服務器,負責管理文件系統的名字空間 (Namespace )及客戶端對文件的訪問。集羣中的DataNode一般是一個節點運行一個DataNode進程,負責管理它所在節點上的存儲。
- NameNode的工作: 一個HDFS集羣包含一個NameNode,是一個主服務器,它用於管理文件系統名稱空間並管理客戶端對文件的訪問。NameNode執行文件系統命名空間操作,如打開,關閉和重命名文件和目錄。 它還確定塊到DataNode的映射。 NameNode是所有HDFS元數據的仲裁者和存儲庫。 該系統的設計方式是用戶數據永遠不會流經NameNode。NameNode控制着關於blocks複製的所有決定。它週期性地接收集羣中DataNode發送的心跳和塊報告。收到心跳意味着DataNode在正常地運行着。一個塊報告包含着DataNode上所有塊信息的集合。
- DataNode的工作:通常是羣集中的每個節點一個DataNode,用於管理連接到它們所運行的節點的存儲。 HDFS公開文件系統名稱空間並允許用戶數據存儲在文件中。 在內部,文件被分成一個或多個塊,這些塊存儲在一組DataNode中。 DataNode負責提供來自文件系統客戶端的讀取和寫入請求。 DataNode還根據來自NameNode的指令執行數據塊創建,刪除和複製。
- namenode的容錯:(1)第一種機制是備份那些組成文件系統元數據持久狀態的文件。這些寫操作是實時同步的,是原子操作。(2)另一種可信的辦法是運行一個輔助namenode,但它不能被用作namenode。這個輔助namenode的重要作用是定期通過編輯日誌合併命名空間鏡像,以防止編輯日誌過大。但是,輔助namenode保存的狀態總是滯後於主節點,所以在主節點全部失效時,難免會丟失部分數據。
- namenode的高可用性:通過配置了一對活動-備用(active-standby)namenode。當活動namenode失效,備用namenode就會接管他的任務並開始服務於來自客戶端的請求,不會有任何明顯中斷。
副本選址策略
namenode如何選擇在那個datanode存儲副本,這裏需要對可靠性、寫入帶寬和讀取帶寬進行權衡。
副本的選址對HDFS的可靠性和性能是起到關鍵作用的。機架感知副本配置策略的目的是提高可靠性、可用性和網絡帶寬的利用率。運行在集羣計算機的大型HDFS實例一般是分佈在許多機架上。兩個不同機架上的節點的通訊必須經過交換機。在大多數情況下,同一個機架上的不同機器之間的網絡帶寬要優於不同機架上的機器的。
通常情況下,當複製因子爲3時,HDFS的副本放置策略是將一個副本放在本機架的一個節點上,將另一個副本放在本機架的另一個節點,最後一個副本放在不同機架的不同節點上。該策略減少機架內部的傳輸以提高寫的性能。這個策略提高了寫性能而不影響數據可靠性和讀性能。爲了最大限度地減少全局帶寬消耗和讀取延遲,HDFS試圖讓讀取者的讀取需求離副本最近。
HDFS不擅長的事情
-
低時間延遲的數據訪問:要求低時間延遲數據訪問的應用,例如幾十毫秒範圍,不適合在HDFS上運行。目前,對於低延遲的訪問需求,HBase是更好的選擇。
- 大量的小文件:由於namenode將文件系統的元數據存儲在內存中,因此該文件系統所能存儲的文件總數受限於namenode的內存容量。
- 多用戶寫入,任意修改文件:他不支持具有多個寫入者的操作,也不支持在文件的任意位置進行修改。
安全模式
在啓動時,NameNode進入一個特殊的狀態稱之爲安全模式。當NameNode進入安全模式之後數據塊的複製將不會發生。NameNode接收來自DataNode的心跳和數據塊報告。數據塊報告包含正在運行的DataNode上的數據塊信息集合。每個塊都指定了最小副本數。一個數據塊如果被NameNode檢查確保它滿足最小副本數,那麼它被認爲是安全的。
NameNode存儲着HDFS的命名空間。NmaeNode使用一個稱之爲EditLog的事務日誌持續地記錄發生在文件系統元數據的每一個改變。NameNode在它本地的系統中用一個文件來存儲EditLog。整個文件系統命名空間,包括blocks的映射關係和文件系統屬性,將儲存在一個叫FsImage的文件。FsImage也是儲存在NameNode所在的本地文件系統中。
NameNode在內存中保存着整個文件系統命名空間的圖像和文件映射關係。當Namenode啓動時,它從硬盤中讀取Edits和FsImage,將所有Edits中的事務作用在內存中的FsImage上,並將這個新版本的FsImage從內存中保存到本地磁盤上,然後刪除舊的Edits,因爲這個舊的Edits的事務都已經作用在FsImage上了。這個過程稱爲一個檢查點(checkpoint)。只有當NameNode啓動時會執行一次。
三.HDFS機制
心跳機制
namenode怎麼 知道datanode的存活狀態?datanode每隔一定時間向namenode發送一次心跳報告,目的就是告訴namenode自己的存活狀態。
這個時間間隔默認是3秒,在hdfs-default.xml中設置,由參數dfs.heartbeat.interval:
<property>
<name>dfs.heartbeat.interval</name>
<value>3</value>
<description>Determines datanode heartbeat interval in seconds.</description>
</property>
namenode什麼時候判定datanode死亡?
datanode每隔3秒向namenode發送心跳報告,如果namenode連續10次沒有收到datanode的心跳報告,則認爲datanode可能死了,但是並沒有斷定其死亡(namenode還需去驗證)。這個時候,namenode向datanode發送一次檢查,發送一次檢查的時間是5min,如果一次檢查沒有返回信息,namenode還會再進行一次檢查,如果再沒有收到信息,namenode就認爲該datanode死亡。
所以,namenode最終判斷datanode死亡(宕機)需要103+560*2 = 630秒。
四.讀流程
- 打開分佈式文件:調用分佈式文件 DistributedFileSystem.open( ) 方法;
- 尋址請求:從 NameNode 處得到 DataNode 的地址,DistributedFileSystem使用 RPC 方式調用了NameNode,NameNode 返回存有該副本的DataNode 地址,DistributedFileSystem 返回了一個輸入流對象(FSDataInputStream),該對象封裝了輸入流 DFSInputStream;
- 連接到DataNode:調用輸入流 FSDataInputStream.read( ) 方法從而讓DFSInputStream 連接到 DataNodes;
- 從 DataNode 中獲取數據:通過循環調用 read( ) 方法,從而將數據從 DataNode 傳輸到客戶端;
- 讀取另外的 DataNode 直到完成:到達塊的末端時候,輸入流 DFSInputStream 關閉與 DataNode 連接, 尋找下一個 DataNode;
- 完成讀取,關閉連接:即調用輸入流 FSDataInputStream.close( );
容錯
讀的過程中,有可能失敗的原因:
- 要讀取的 DataNode 存在故障,導致連接不上
- 讀取 block 時,進行 checksum 驗證不通過
這兩種情況,都會從 block 的其他備份所在的 DataNode 繼續讀取數據,不過如果是 block 出問題還會上報給 NameNode, NameNode 會標記該 block 已經損壞,然後複製 block 達到預期設置的文件備份數 。
五.寫流程
- 客戶端調用 DistributedFileSystem 的 create() 方法,開始創建新文件:DistributedFileSystem 創建 DFSOutputStream,產生一個 RPC 調用,讓 NameNode 在文件系統的命名空間中創建這一新文件;
- NameNode 接收到用戶的寫文件的 RPC 請求後,先要執行各種檢查,如客戶是否有相關的創建權限和該文件是否已存在等,檢查都通過後纔會創建一個新文件,並將操作記錄到編輯日誌,然後 DistributedFileSystem會將 DFSOutputStream 對象包裝在 FSDataOutStream 實例中,返回客戶端;否則文件創建失敗並且給客戶端拋 IOException。
- 客戶端開始寫文件:DFSOutputStream 會將文件分割成 packets 數據包(一般爲 64K),然後將這些 packets 寫到其內部的一個叫做 data queue(數據隊列)。data queue 會向 NameNode 節點請求適合存儲數據副本的 DataNode 節點的列表,然後這些 DataNode 之前生成一個 Pipeline 數據流管道,我們假設副本集參數被設置爲 3,那麼這個數據流管道中就有 3 個 DataNode 節點。
- 首先 DFSOutputStream 會將 packets 向 Pipeline 數據流管道中的第一個 DataNode 節點寫數據,第一個DataNode 接收 packets 然後把 packets 寫向 Pipeline 中的第二個節點,同理,第二個節點保存接收到的數據然後將數據寫向 Pipeline 中的第三個 DataNode 節點。
- DFSOutputStream 內部同樣維護另外一個內部的寫數據確認隊列—— ack queue 。當 Pipeline 中的第三個 DataNode 節點將 packets 成功保存後,該節點回向第二個 DataNode 返回一個確認數據寫成功的信息,第二個 DataNode 接收到該確認信息後在當前節點數據寫成功後也會向 Pipeline 中第一個 DataNode 節點發送一個確認數據寫成功的信息,然後第一個節點在收到該信息後如果該節點的數據也寫成功後,會將 packets 從 ack queue 中將數據刪除。
- 完成寫操作後,客戶端調用 close() 關閉寫操作,刷新數據;
- 在數據刷新完後 NameNode 後關閉寫操作流。到此,整個寫操作完成。
簡單總結如下:
- 客戶端調用 create 方法,RPC 調用 NameNode 在命名空間中創建文件;
- NameNode 做權限和文件存在檢查,通過則創建文件並寫日誌,否則返回異常;
- 將文件分割爲數據包並寫入 data queue,向 NameNode 請求將要寫入的 DataNode 節點並生成一個 pipeline;
- 依次向 pipeline 中的每一個 DataNode 節點寫數據;
- 同時維護一個 ack queue,寫入成功則從隊列中移除;
- 調用 close 方法,刷新數據;
- 關閉流。
容錯
在寫數據的過程中,如果其中一個 DataNode 節點寫失敗了會怎樣?
1) 管道關閉。
2)正常的datanode上的當前block會有一個新ID,並將該ID傳送給namenode,以便失敗的datanode在恢復後可以刪除那個不完整的block。
3) 失敗的datanode會被移出管道,餘下的數據塊繼續寫入管道的其他兩個正常的datanode。
4) namenode會標記這個block的副本個數少於指定值。block的副本會稍後在另一個datanode創建。
5)有些時候多個datanode會失敗,但非常少見。只要dfs.replication.min(缺省是1)個datanode成功了,整個寫入過程就算成功。缺少的副本會在集羣中異步的複製,直到達到正常的副本數。
6. 當client完成了所有block的寫入後,調用FSDataOutputStream的close()方法關閉文件。
7. FSDataOutputStream通知namenode寫文件結束。
六.HDFS數據完整性
用戶肯定都希望系統在存儲和處理數據時,數據不會有任何丟失或損壞。但是,受網絡不穩定、硬件損壞等因素,IO操作過程中難免會出現數據丟失或髒數據,難免會出現數據丟失或髒數據,數據傳輸的量越大,出現錯誤的概率就越高。
檢測數據是否損壞的常見措施是,在數據第一次引入系統時計算校驗和(checksum)並存儲,在數據進行傳輸後再次計算校驗和進行對比,如果計算所得的新校驗和和原來的校驗和不匹配,就認爲數據已損壞。但該技術並不能修復數據——它只能檢測出數據錯誤。(這正是不使用低端硬件的原因。具體說來,一定要使用ECC內存。)注意,校驗和也是可能損壞的,不只是數據,但由於校驗和比數據小得多,所以損壞的可能性非常小。
(1)對本地文件I/O的檢查
在Hadoop中,本地文件系統的數據完整性由客戶端負責。重點在於存車讀取文件時進行校驗和的處理。
具體做法是:每當hadoop創建文件a時,hadoop就會同時在同一個文件夾下創建隱藏文件.a.crc,這個文件記錄了 文件a的校驗和。針對數據文件的大小,每512個字節會生成一個32位的校驗和(4字節),可以在src/core/core-default.xml中通過修改io.bytes.per.checksum的大小來修改每個校驗和所針對的文件的大小。
在hadoop中,校驗和系統單獨爲一類,org.apache.hadoop.fs.ChecksumFileSystem,當需要校驗和機制時,可以很方便的調用它來服務。
(2)對HDFS的I/O數據進行檢查
一般來說,HDFS會在三種情況下檢驗校驗和:
DataNode接收數據後存儲數據前
DataNode接收數據一般有兩種情況:1.從客戶端上傳數據 2.DataNode從其他DataNode上接收數據。
當客戶端上傳數據時,正在寫數據的客戶端將數據及其校驗和發送到由一系列datanode組成的Pipeline管線。Pipeline管線中最後一個datanode負責驗證校驗和。
DataNode數據存儲步驟:(包括從DataNode和客戶端兩種傳輸方式)
1.在傳輸數據的最開始階段,Hadoop會簡單地檢查數據塊的完整性信息;
2.依次向各個DataNode傳輸數據,包括數據頭信息、塊信息、備份個數、校驗和等;
3.Hadoop不會在數據每流動到一個DataNode都檢查校驗和,只會在數據流達到最後一個節點時才檢查校驗和
如果在驗證過程中發現有不一致的塊,就會拋出CheckSumException異常信息
客戶端讀取DataNode上的數據時
Hadoop會在客戶端讀取DataNode上的數據時,使用DFSClient中的read函數先將數據讀入到用戶的數據緩衝區,然後再檢查校驗和。將他們與datanode中存儲的校驗和進行比較
每個datanode均持久保存有一個用於驗證的校驗和日誌,所以它知道每個數據塊的最後一次驗證時間
客戶端成功驗證一個數據塊後,會告訴這個datanode,datanode由此更新日誌
DataNode後臺守護進程的定期檢查
DataNode會在後臺運行DataBlockScanner,這個程序定期驗證存儲在這個datanode上的所有數據塊(3周)
該項措施是解決物理存儲媒體上位衰減,位損壞的有力措施。
Hadoop處理損壞數據的機制:
DataNode在讀取block塊的時候會先進行checksum(數據塊校驗和)
如果client發現本次計算的校驗和跟創建時的校驗和不一致,則認爲該block塊已損壞
客戶端在拋出ChecksumException之前上報該block信息給namenode進行標記(“已損壞”)
這樣namenode就不會把客戶端指向這個block,也不會複製這個block到其他的datanode。
client重新讀取另外的datanode上的block
在心跳返回時NameNode將塊的複製任務交給DataNode,從完好的block副本進行復制以達到默認的備份數3
NameNode刪除掉壞的block。
DataNode在一個block塊被創建之日起三週後開始進行校驗
如果出於一些原因在操作的時候不想讓hdfs檢查校驗碼
在FileSystem的open()之前通過設置FileSystem的setVerifyCheckSum(false)方法禁用校驗和
或者命令行使用get時候添加選項-ignoreCrc或者直接使用-copyToLocal
fs.setVerifyChecksum(false) fs.open(new Path(“”)) // 就不進行校驗檢查了
Hadoop fs –get –ignoreCrc hdfs://master:9000/a.txt
Hadoop fs –copyToLocal hdfs://master:9000/a.txt
CheckSum校驗原理
Hadoop數據的完整性檢測,都是通過校驗和的比較來完成,在創建新文件時(也就是在上傳數據到hdfs上時)將校驗和的值和數據一起保存起來。NameNode會收到來自client、DataNode的檢驗和信息,根據這兩個信息來維護文件的塊存儲及向客戶端提供塊讀取服務。
HDFS會對寫入的所有數據計算校驗和,並在讀取數據時驗證校驗和。
常用的錯誤檢測碼是CRC-32(循環冗餘校驗),任何大小的數據輸入均計算得到一個32位的整數校驗和。
在寫入文件時,hdfs爲每個數據塊都生成一個crc文件。客戶端讀取數據時生成一個crc與數據節點存儲的crc做比對,如果不匹配則說明數據已經損壞了。數據節點在後臺運行一個程序定期(默認爲21天)檢測數據,防止物理存儲介質中位衰減而造成的數據損壞。
DataNode在寫入時計算出校驗和,然後每次讀的時候再計算校驗和進行檢驗
hdfs會爲每一個固定長度的數據(一個個數據包)執行一次校驗和
這個值由io.bytes.per.checksum指定,默認是512字節。
因爲CRC32是32位即4個字節,這樣校驗和佔用的空間就會少於原數據的1%。
datanode在存儲收到的數據前會校驗數據的校驗和,比如收到客戶端的數據或者其他副本傳過來的數據。
如hdfs數據流中客戶端寫入數據到hdfs時的數據流,在管道的最後一個datanode會去檢查這個校驗和
如果發現錯誤,就會拋出ChecksumException到客戶端
從datanode讀數據的時候一樣要檢查校驗和,而且每個datanode還保存了檢查校驗和的日誌,每次校驗都會記錄到日誌中
除了讀寫操作會檢查校驗和以外,datanode還跑着一個後臺進程(DataBlockScanner)
定期校驗存在在它上面的block,因爲除了讀寫過程中會產生數據錯誤以外,硬件本身也會產生數據錯誤,比如位衰減(bit rot)