1. 程式人生 > >Hadoop權威指南讀書筆記(1) - MapReduce和HDFS簡介

Hadoop權威指南讀書筆記(1) - MapReduce和HDFS簡介

最近開始讀<< Hadoop:the definitive guide>>,於是打算寫點讀書筆記,書電子版見網盤,密碼v66s。

原書推薦的讀書順序如下圖:
這裡寫圖片描述
這裡我們就按從第一章到最後一章的順序讀吧.

Chapter 2: MapReduce

mapreduce思想

MR的思想非常簡單,如下圖所示:
這裡寫圖片描述

  1. Map將按照行讀入輸入檔案,然後將它們parse為若干個< Key,Value>對
  2. 將map生成的KV對打亂並按照Key 排序
  3. Reduce將所有key相同的KV對聚集在一起,組成一個< K, List< Value>>對列,並獲得List< Value>的Iterator,然後再進一步處理,最後輸出新的KV對到檔案中。

mapreduce程式的實現

MapReduce一般用Java實現,故我們需要注意以下的地方:

  • Map輸出的KV對的資料型別必須和reduce接受的輸入型別一致。
  • 輸入可以用addInputPath定義,引數可以是一個檔案,一個目錄,或者一個檔案匹配模式。
  • 輸出用setOutputPath定義,引數必須是一個不存在的目錄
  • 我們需要用setOutputKey和setOutputValue來設定reduce的輸出型別。如果map的輸出型別和reduce輸出型別一樣,則不需要再設定,否則用setMapOutputKey等
  • 最後waitForCompletion()返回job是否執行成功,並且接受一個boolean引數,如果為真則列印程序執行資訊,否則不列印。

mapreduce的資料流

我們需要注意的以下幾點:

  • Hadoop將輸入分割成若干個大小固定的塊,稱之為splits,每一個splits對應一個map task.
  • 在建立map task時,hadoop會遵循data locality optimization。即優先考慮儲存對應splits的機器,如果不行則考慮同一個機架上的機器,這兩個都不滿足,才會去考慮其他機架上的機器。如下圖:
    這裡寫圖片描述
  • map task將它的輸出寫在本地磁碟,而不是HDFS,因為這些資料都是暫時的,只需要作為輸入提供給reduce,並不是最終結果。

    此外還需要說明一點,在一些情況下,我們可以實現一個combiner函式,這個函式工作在每一個map之後,將map函式的輸出先聚合好,再將其輸出到reduce中。這樣可以將map輸出的中間結果規模減小,減少和reduce通訊的規模。

Chapter 3: The Hadoop Distributed Filesystem

HDFS的設計和基本概念

HDFS被設計用於下面三個目的:

  • 大檔案(very large files):幾百M,G,T甚至更多。
  • 流式資料訪問(Streaming data access):由於大部分資料訪問場景都是寫入一次,讀取多次,故讀取整個資料的時間比讀區第一條記錄的延遲要重要得多。
  • 廉價硬體(Commodity hardware):Hadoop叢集往往執行在大量的廉價硬體上(例如機械硬碟),因此節點出錯的機率非常高。HDFS要求在使用者不察覺到出錯的情況下完成工作。

綜上所述,HDFS不適用於下面的情況:

  • 低延遲訪問(low-latency data access): Hbase更適合低延遲訪問。
  • 大量小檔案(lots of small files): namenode把檔案的元資料都儲存在記憶體(每條檔案,目錄和塊都佔大約150bytes),大量的小檔案會給namenode的記憶體帶來巨大壓力。
  • 多個寫入者和檔案修改(Multiple writes and arbitrary file modifications): HDFS只支援append的修改操作。

塊(Block)

磁碟有一個Block的概念,它是磁碟讀/寫資料的最小單位,一般為512bytes。HDFS也有Block的概念,但它的塊是一個很大的單元,預設是64MB。像硬碟中的檔案系統一樣,在HDFS中的檔案將會按塊大小進行分解,並作為獨立的單元進行儲存。但和硬碟中的檔案系統不一樣的是,儲存在塊中的一個比塊小的檔案並不會佔據一個塊大小盤物理空間(HDFS中一個塊只儲存一個檔案的內容)。

HDFS中塊之所以這麼大主要有兩個原因:

  1. 較大的塊可以在讀取相同大小資料的前提下,尋道時間更小。是的資料傳輸總時間更少。
  2. 但塊的大小也不宜過大,因為mapreduce的map一般每次處理一個塊。太大的塊會導致塊數目變少,從而map個數變少,降低mapreduce效能。

HDFS中塊的好處:

  1. 檔案可以任意大:檔案被拆分成塊儲存起來,而所有的塊不需要在同一個節點上。
  2. 簡化了系統的設計:快大小的固定使得每塊硬碟可以儲存多少個塊變得非常容易計算。
  3. 更利於資料備份:每個塊都可以單獨做備份。

名位元組點(namenode)和資料節點(datanode)

HDFS中的節點是master-slave的工作模式,因此分為兩種: 一個namenode 和多個 datanode。

Namenode負責管理檔案系統名稱空間,例如維護檔案系統的樹結構,還有樹中所有檔案和目錄的元資料。這些資訊分為兩個部分: fsimage和edit log,前者是一個對hdfs檔案系統的快照而後者是檔案系統的改動序列。此外它知道一個檔案的全部塊在哪些datanode上。

Datanode則負責儲存和讀取這些blocks。此外他們還要定期向namenode報告儲存的blocks的情況。

由於沒有namenode, hdfs就不知道檔案的組織結構,因此hadoop1.0中對namenode提供了兩個容錯機制:

  • 一個是namenode將自己的狀態備份到多個檔案系統中。這些備份過程是原子且非同步的,而且一般被備份到本地磁碟。
  • 還有一個是執行一個secondary namenode(之前那個namenode被稱為primary namenode)。這個s-node並不能單純看作p-node的備份節點。事實上s-node會定期訪問p-node的fsimage和edit log並將其合併成新的fsimage,從而保證edit log不會太大。

但是這種primary-secondary的備份方式仍然不夠好,實際上顯然namenode仍然是單點失敗的(single point of failure) ,因此如果它失敗時從secondary恢復可能需要很久(如半個小時)。在hadoop2.0中提供了新的解決辦法,即提供了對HA(high availability)的支援:active-standby namenodes。這種新的容錯機制有和上面的相比如下特點:

  • edit logs被儲存在一個共享空間裡面,這樣active和standby的namenodes都可以訪問它們。從而在兩者的記憶體中都有最新的資料。
  • datanodes必須定期向兩個namenodes傳送blocks報告。
  • 原來secondary namenode的角色被standby namenode替換。

這裡的共享空間除了用NFS儲存,hadoop本身還提供了QJM(quorum journal manager),這也是它推薦的方法。QJM使用了paxos協議,因此只要保證過半的儲存節點沒有失敗,就不會導致edit logs丟失。利用了上面新的設計後,standby namenode可以在很多時間內(例如幾十秒)接管active namenode的工作,原因是edit logs仍然在記憶體中,並且standby namenode中有最新的block mapping。

最後,如果standby namenode失敗了,則直接從hadoop 叢集中冷啟動即可。這不會比non-HA的效果更差。

命令列和API

這部分略過,因為網上有很多相關部落格,只說一點:
副本的概念對目錄是無效的,因為它們的資訊都用元資料儲存在namenode中

資料流

HDFS讀寫的過程見下面兩張圖,十分簡單直接,這裡不再贅述,有興趣可以查閱其他部落格。
這裡寫圖片描述
這裡寫圖片描述

這裡簡單展示一下hadoop的節點距離和3副本備份策略:
這裡寫圖片描述
這裡寫圖片描述
實際上hadoop先把第一個副本存在client所在的那個namenode(如果是外部的client則隨機選取一個),然後把第二和第三個副本存在其他機架上的兩個不同的node中。至於更多的副本則隨機儲存。

我們還需要介紹一下一致性模型(coherency model),即檔案在讀寫時的資料可見性。HDFS中,所有block只有在寫完後才對使用者可見,如果它正在寫則是使用者不可見的。因此HDFS提供了兩個函式來幫助使用者:

  • hflush(): 保證之前寫入的資料都對使用者可見。
  • hsync(): 不僅僅保證可見,還將其寫入永續性介質(如硬碟)。

最後我們在提一個hdfs指令:
hadoop distcp < dir1 > < dir2 >
這是hdsf並行地拷貝檔案,往往在大規模資料遷移時很有用。