天天看點

帶你讀《HBase原理與實踐》之三:HBase依賴服務HBase依賴服務

點選檢視第一章 點選檢視第二章

第3章

HBase依賴服務

HBase并不是一個獨立的無依賴的項目。在正常的線上叢集上,它至少依賴于ZooKeeper、HDFS兩個Apache頂級項目。對于某些特殊場景,例如Copy Snapshot和驗證叢集間資料一緻性等,還需要借助Yarn叢集的分布式計算能力才能實作。

正是借助了Apache的這些成熟穩定的頂級系統,HBase研發團隊才能夠集中精力來解決高性能、高可用的KV存儲系統所面臨的諸多問題。

本章将簡要介紹ZooKeeper和HDFS,以便讀者更深入地了解HBase内部原理。

3.1 ZooKeeper簡介

ZooKeeper在HBase系統中扮演着非常重要的角色。事實上,無論在HBase中,還是在Hadoop其他的分布式項目中,抑或是非Hadoop生态圈的很多開源項目中,甚至是全球大大小小的公司内,ZooKeeper都是一項非常重要的基礎設施。

ZooKeeper之是以占據如此重要的地位,是因為它解決了分布式系統中一些最基礎的問題:

  • 提供極低延遲、超高可用的記憶體KV資料庫服務。
  • 提供中心化的服務故障發現服務。
  • 提供分布式場景下的鎖、Counter、Queue等協調服務。

ZooKeeper叢集本身是一個服務高可用的叢集,通常由奇數個(比如3個、5個等)節點組成,叢集的服務不會因小于一半的節點當機而受影響。ZooKeeper叢集中多個節點都存儲同一份資料,為保證多節點之間資料的一緻性,ZooKeeper使用ZAB(ZooKeeper Atomic Broadcast)協定作為資料一緻性的算法。ZAB是由Paxos算法改進而來,有興趣的讀者可以進一步閱讀論文《Zab: High-performance broadcast for primary-backup systems》。

ZooKeeper節點内資料組織為樹狀結構,如圖3-1所示,資料存儲在每一個樹節點(稱為znode)上,使用者可以根據資料路徑擷取對應的資料。

1. ZooKeeper核心特性

ZooKeeper在使用ZAB協定保證多節點資料一緻性的基礎上實作了很多其他工程特性,以下這些特性對于實作分布式叢集管理的諸多功能至關重要。

帶你讀《HBase原理與實踐》之三:HBase依賴服務HBase依賴服務

圖3-1    ZooKeeper節點内資料組織結構

1)多類型節點。ZooKeeper資料樹節點可以設定多種節點類型,每種節點類型具有不同節點特性。

  • 持久節點(PERSISTENT):節點建立後就會一直存在,直到有删除操作來主動清除這個節點。
  • 臨時節點(EPHEMERAL):和持久節點不同,臨時節點的生命周期和用戶端session綁定。也就是說,如果用戶端session失效,那麼這個節點就會自動被清除掉。注意,這裡提到的是session失效,而非連接配接斷開,後面會講到兩者的差別;另外,在臨時節點下面不能建立子節點。
  • 持久順序節點(PERSISTENT_SEQUENTIAL):這類節點具有持久特性和順序特性。持久特性即一旦建立就會一直存在,直至被删除。順序特性表示父節點會為它的第一級子節點維護一份時序,記錄每個子節點建立的先後順序。實際實作中,Zookeeper會為順序節點加上一個自增的數字字尾作為新的節點名。
  • 臨時順序節點(EPHEMERAL_SEQUENTIAL):這類節點具有臨時特性和順序特性。臨時特性即用戶端session一旦結束,節點就消失。順序特性表示父節點會為它的第一級子節點維護一份時序,記錄每個子節點建立的先後順序。

2)Watcher機制。Watcher機制是ZooKeeper實作的一種事件異步回報機制,就像現實生活中某讀者訂閱了某個主題,這個主題一旦有任何更新都會第一時間回報給該讀者一樣。

  • watcher設定:ZooKeeper可以為所有的讀操作設定watcher,這些讀操作包括getChildren()、exists()以及getData()。其中通過getChildren()設定的watcher為子節點watcher,這類watcher關注的事件包括子節點建立、删除等。通過exists()和getData()設定的watcher為資料watcher,這類watcher關注的事件包含節點資料發生更新、子節點發生建立删除操作等。
  • watcher觸發回報:ZooKeeper中用戶端與伺服器端之間的連接配接是長連接配接。watcher事件發生之後伺服器端會發送一個資訊給用戶端,用戶端會調用預先準備的處理邏輯進行應對。
  • watcher特性:watcher事件是一次性的觸發器,當watcher關注的對象狀态發生改變時,将會觸發此對象上所設定的watcher對應事件。例如:如果一個用戶端通過getData("/znode1", true)操作給節點/znode1加上了一個watcher,一旦"/znode1"的資料被改變或删除,用戶端就會獲得一個關于"znode1"的事件。但是如果/znode1再次改變,那麼将不再有watcher事件回報給用戶端,除非用戶端重新設定了一個watcher。

3)Session機制。ZooKeeper在啟動時,用戶端會根據配置檔案中ZooKeeper伺服器清單配置項,選擇其中任意一台伺服器相連,如果連接配接失敗,它會嘗試連接配接另一台伺服器,直到與一台伺服器成功建立連接配接或因為所有ZooKeeper伺服器都不可用而失敗。

一旦建立連接配接,ZooKeeper就會為該用戶端建立一個新的session。每個session都會有一個逾時時間設定,這個設定由建立session的應用和伺服器端設定共同決定。如果ZooKeeper伺服器在逾時時間段内沒有收到任何請求,則相應的session會過期。一旦session過期,任何與該session相關聯的臨時znode都會被清理。臨時znode一旦被清理,注冊在其上的watch事件就會被觸發。

需要注意的是,ZooKeeper對于網絡連接配接斷開和session過期是兩種處理機制。在用戶端與服務端之間維持的是一個長連接配接,在session逾時時間内,服務端會不斷檢測該用戶端是否還處于正常連接配接,服務端會将用戶端的每次操作視為一次有效的心跳檢測來反複地進行session激活。是以,在正常情況下,用戶端session是一直有效的。然而,當用戶端與服務端之間的連接配接斷開後,使用者在用戶端可能主要看到:CONNECTION_LOSS和SESSION_EXPIRED兩類異常。

  • CONNECTION_LOSS:網絡一旦斷連,用戶端就會收到CONNECTION_LOSS異常,此時它會自動從ZooKeeper伺服器清單中重新選取新的位址,并嘗試重新連接配接,直到最終成功連接配接上伺服器。
  • SESSION_EXPIRED:用戶端與服務端斷開連接配接後,如果重連時間耗時太長,超過了session逾時時間,伺服器會進行session清理。注意,此時用戶端不知道session已經失效,狀态還是DISCONNECTED,如果用戶端重新連上了伺服器,此時狀态會變更為SESSION_EXPIRED。

2. ZooKeeper典型使用場景

ZooKeeper在實際叢集管理中利用上述工程特性可以實作非常多的分布式功能,比如HBase使用ZooKeeper實作了Master高可用管理、RegionServer當機異常檢測、分布式鎖等一系列功能。以分布式鎖為例,具體實作步驟如下:

1)用戶端調用create()方法建立名為“locknode/lock-”的節點,需要注意的是,節點的建立類型需要設定為EPHEMERAL_SEQUENTIAL。

2)用戶端調用getChildren(“locknode”)方法來擷取所有已經建立的子節點。

3)用戶端擷取到所有子節點path之後,如果發現自己在步驟1)中建立的節點序号最小,那麼就認為這個用戶端獲得了鎖。

4)如果在步驟3)中發現自己并非所有子節點中最小的,說明叢集中其他程序擷取到了這把鎖。此時用戶端需要找到最小子節點,然後對其調用exist()方法,同時注冊事件監聽。

5)一旦最小子節點對應的程序釋放了分布式鎖,對應的臨時節點就會被移除,用戶端因為注冊了事件監聽而收到相應的通知。這個時候用戶端需要再次調用getChildren("locknode")方法來擷取所有已經建立的子節點,然後進入步驟3。

3.2 HBase中ZooKeeper核心配置

一個分布式HBase叢集的部署運作強烈依賴于ZooKeeper,在目前的HBase系統實作中,ZooKeeper扮演了非常重要的角色。

首先,在安裝HBase叢集時需要在配置檔案conf/hbase-site.xml中配置與ZooKeeper相關的幾個重要配置項,如下所示:

<name>hbase.zookeeper.quorum</name>
<value>localhost</value>           
<name>hbase.zookeeper.property.clientPort</name>
<value>2181</value>           
<name>zookeeper.znode.parent</name>
<value>/hbase</value>           

其中,hbase.zookeeper.quorum為ZooKeeper叢集的位址,必須進行配置,該項預設為localhost。hbase.zookeeper.property.clientPort預設為2181,可以不進行配置。zookeeper.znode.parent預設為/hbase,可以不配置。HBase叢集啟動之後,使用用戶端進行讀寫操作時也需要配置上述ZooKeeper相關參數。

其次,還有一個與ZooKeeper相關的配置項非常重要—zookeeper.session.timeout,表示RegionServer與ZooKeeper之間的會話逾時時間,一旦session逾時,ZooKeeper就會感覺到,通知Master将對應的RegionServer移出叢集,并将該RegionServer上所有Region遷移到叢集中其他RegionServer。

在了解了HBase叢集中ZooKeeper的基本配置之後,再來看看HBase在ZooKeeper上都存儲了哪些資訊。下面内容是HBase在ZooKeeper根節點下建立的主要子節點:

[zk: localhost:2181(CONNECTED) 2] ls /hbase

[meta-region-server, backup-masters, table, region-in-transition, table-lock, master, balancer, namespace, hbaseid, online-snapshot, replication, splitWAL, recovering-regions, rs]

具體說明如下。

  • meta-region-server:存儲HBase叢集hbase:meta中繼資料表所在的RegionServer通路位址。用戶端讀寫資料首先會從此節點讀取hbase:meta中繼資料的通路位址,将部分中繼資料加載到本地,根據中繼資料進行資料路由。
  • master/backup-masters:通常來說生産線環境要求所有元件節點都避免單點服務,HBase使用ZooKeeper的相關特性實作了Master的高可用功能。其中Master節點是叢集中對外服務的管理伺服器,backup-masters下的子節點是叢集中的備份節點,一旦對外服務的主Master節點發生了異常,備Master節點可以通過選舉切換成主Master,繼續對外服務。需要注意的是備Master節點可以是一個,也可以是多個。
  • table:叢集中所有表資訊。
  • region-in-transition:在目前HBase系統實作中,遷移Region是一個非常複雜的過程。首先對這個Region執行unassign操作,将此Region從open狀态變為offline狀态(中間涉及PENDING_CLOSE、CLOSING以及CLOSED等過渡狀态),再在目标RegionServer上執行assign操作,将此Region從offline狀态變成open狀态。這個過程需要在Master上記錄此Region的各個狀态。目前,RegionServer将這些狀态通知給Master是通過ZooKeeper實作的,RegionServer會在region-in-transition中變更Region的狀态,Master監聽ZooKeeper對應節點,以便在Region狀态發生變更之後立馬獲得通知,得到通知後Master再去更新Region在hbase:meta中的狀态和在記憶體中的狀态。
  • table-lock:HBase系統使用ZooKeeper相關機制實作分布式鎖。HBase中一張表的資料會以Region的形式存在于多個RegionServer上,是以對一張表的DDL操作(建立、删除、更新等操作)通常都是典型的分布式操作。每次執行DDL操作之前都需要首先擷取相應表的表鎖,防止多個DDL操作之間出現沖突,這個表鎖就是分布式鎖。分布式鎖可以使用ZooKeeper的相關特性來實作,在此不再贅述。
  • online-snapshot:用來實作線上snapshot操作。表級别線上snapshot同樣是一個分布式操作,需要對目标表的每個Region都執行snapshot,全部成功之後才能傳回成功。Master作為控制節點給各個相關RegionServer下達snapshot指令,對應RegionServer對目标Region執行snapshot,成功後通知Master。Master下達snapshot指令、RegionServer回報snapshot結果都是通過ZooKeeper完成的。

    replication:用來實作HBase複制功能。

  • splitWAL/recovering-regions:用來實作HBase分布式故障恢複。為了加速叢集故障恢複,HBase實作了分布式故障恢複,讓叢集中所有RegionServer都參與未回放日志切分。ZooKeeper是Master和RegionServer之間的協調節點。
  • rs:叢集中所有運作的RegionServer。

    思考與練習

[1] 嘗試動手搭建一個3節點的高可用ZooKeeper叢集,并編寫搭建文檔。若暫時沒有3台以上的不同機器,可以在一台機器上啟動3個不同端口的ZooKeeper程序,每個程序表示一台機器上的ZooKeeper節點.這種模式在ZooKeeper内部會被認為是一個分布式的ZooKeeper叢集,但是實際線上并不是(因為這個機器挂了,ZK叢集就不可用)。我們把這種模式稱為“僞分布式叢集”,僅用于測試。

[2] 事實上,Hadoop諸多項目的UT都是通過編寫代碼搭建“僞分布式叢集”的方式來模拟叢集測試的。請編寫一個UT,用來驗證Zookeeper讀寫znode的正确性。

3.3 HDFS簡介

根據1.3節的内容,我們知道HBase的檔案都存放在HDFS(Hadoop Distribuited File System)檔案系統上。HDFS本質上是一個分布式檔案系統,可以部署在大量價格低廉的伺服器上,提供可擴充的、高容錯性的檔案讀寫服務。HBase項目本身并不負責檔案層面的高可用和擴充性,它通過把資料存儲在HDFS上來實作大容量檔案存儲和檔案備份。

與其他的分布式檔案系統相比,HDFS擅長的場景是大檔案(一般認為位元組數超過數十MB的檔案為大檔案)的順序讀、随機讀和順序寫。從API層面,HDFS并不支援檔案的随機寫(Seek+Write)以及多個用戶端同時寫同一個檔案。正是由于HDFS的這些優點和缺點,深刻地影響了HBase的設計。

1. HDFS架構

HDFS架構如圖3-2所示。

一般情況下,一個線上的高可用HDFS叢集主要由4個重要的服務組成:NameNode、DataNode、JournalNode、ZkFailoverController。在具體介紹4個服務之前,我們需要了解Block這個概念。存儲在HDFS上面的檔案實際上是由若幹個資料塊(Block,大小預設為128MB)組成,每一個Block會設定一個副本數N,表示這個Block在寫入的時候會寫入N個資料節點,以達到資料備份的目的。讀取的時候隻需要依次讀取組成這個檔案的Block即可完整讀取整個檔案,注意讀取時隻需選擇N個副本中的任何一個副本進行讀取即可。

(1)NameNode

線上需要部署2個NameNode:一個節點是Active狀态并對外提供服務;另一個節點是StandBy狀态,作為Active的備份,備份狀态下不提供對外服務,也就是說HDFS用戶端無法通過請求StandBy狀态的NameNode來實作修改檔案中繼資料的目的。如果ZkFailoverController服務檢測到Active狀态的節點發生異常,會自動把StandBy狀态的NameNode服務切換成Active的NameNode。

帶你讀《HBase原理與實踐》之三:HBase依賴服務HBase依賴服務

圖3-2 HDFS架構

NameNode存儲并管理HDFS的檔案中繼資料,這些中繼資料主要包括檔案屬性(檔案大小、檔案擁有者、組以及各個使用者的檔案通路權限等)以及檔案的多個資料塊分布在哪些存儲節點上。需要注意的是,檔案中繼資料是在不斷更新的,例如HBase對HLog檔案持續寫入導緻檔案的Block數量不斷增長,管理者修改了某些檔案的通路權限,HBase把一個HFile從/hbase/data目錄移到/hbase/archive目錄。所有這些操作都會導緻檔案中繼資料的變更。是以NameNode本質上是一個獨立的維護所有檔案中繼資料的高可用KV資料庫系統。為了保證每一次檔案中繼資料都不丢失,NameNode采用寫EditLog和FsImage的方式來保證中繼資料的高效持久化。每一次檔案中繼資料的寫入,都是先做一次EditLog的順序寫,然後再修改NameNode的記憶體狀态。同時NameNode會有一個内部線程,周期性地把記憶體狀态導出到本地磁盤持久化成FsImage(假設導出FsImage的時間點為t),那麼對于小于時間點t的EditLog都認為是過期狀态,是可以清理的,這個過程叫做推進checkpoint。

NameNode會把所有檔案的中繼資料全部維護在記憶體中。是以,如果在HDFS中存放大量的小檔案,則造成配置設定大量的Block,這樣可能耗盡NameNode所有記憶體而導緻OOM。是以,HDFS并不适合存儲大量的小檔案。當然,後續的HDFS版本支援NameNode對中繼資料分片,解決了NameNode的擴充性問題。

(2)DataNode

組成檔案的所有Block都是存放在DataNode節點上的。一個邏輯上的Block會存放在N個不同的DataNode上。而NameNode、JournalNode、ZKFailoverController服務都是用來維護檔案中繼資料的。

(3)JournaNode

由于NameNode是Active-Standby方式的高可用模型,且NameNode在本地寫EditLog,那麼存在一個問題—在StandBy狀态下的NameNode切換成Active狀态後,如何才能保證新Active的NameNode和切換前Active狀态的NameNode擁有完全一緻的資料?如果新Active的NameNode資料和老Active的NameNode不一緻,那麼整個分布式檔案系統的資料也将不一緻,這對使用者來說是一件極為困擾的事情。

為了保證兩個NameNode在切換前後能讀到一緻的EditLog,HDFS單獨實作了一個叫做JournalNode的服務。線上叢集一般部署奇數個JournalNode(一般是3個,或者5個),在這些JournalNode内部,通過Paxos協定來保證資料一緻性。是以可以認為,JournalNode其實就是用來維護EditLog一緻性的Paxos組。

(4)ZKFailoverController

ZKFailoverController主要用來實作NameNode的自動切換。

2.檔案寫入

下面是一個非常典型的例子,在HDFS上建立檔案并寫入一段資料:

FileSystem fs=FileSystem.get(conf);

FSDataOutputStream out ;

try(out = fs.create(path)){

out.writeBytes("test-data");           

}

注意,try(out=fs.create(path))這行代碼相當于在try{}代碼塊結束後,會在finally代碼塊中執行out.close(),其中out.close()内部會先執行out.hflush(),然後關閉相關資源。

我們用這個簡單的例子來闡述HDFS的檔案寫入流程,如圖3-3所示。

帶你讀《HBase原理與實踐》之三:HBase依賴服務HBase依賴服務

圖3-3 HDFS檔案寫入流程

(1)寫入流程描述

1)DFS Client在建立FSDataOutputStream時,把檔案中繼資料發給NameNode,得到一個檔案唯一辨別的f?ileId,并向使用者傳回一個OutputStream。

2)使用者拿到OutputStream之後,開始寫資料。注意寫資料都是按照Block來寫的,不同的Block可能分布在不同的DataNode上,是以如果發現目前的Block已經寫滿,DFSClient就需要再發起請求向NameNode申請一個新的Block。在一個Block内部,資料由若幹個Packet(預設64KB)組成,若目前的Packet寫滿了,就放入DataQueue隊列,DataStreamer線程異步地把Packet寫入到對應的DataNode。3個副本中的某個DataNode收到Packet之後,會先寫本地檔案,然後發送一份到第二個DataNode,第二個執行類似步驟後,發給第三個DataNode。等所有的DataNode都寫完資料之後,就發送Packet的ACK給DFS Client,隻有收到ACK的Packet才是寫入成功的。

3)使用者執行完寫入操作後,需要關閉OutputStream。關閉過程中,DFSClient會先把本地DataQueue中未發出去的Packet全部發送到DataNode。若忘記關閉,對那些已經成功緩存在DFS Client的DataQueue中但尚未成功寫入DataNode的資料,将沒有機會寫入DataNode中。對使用者來說,這部分資料将丢失。

(2)FSDataOutputStream中hf?lush和hsync的差別

hflush成功傳回,則表示DFSClient的DataQueue中所有Packet都已經成功發送到了3個DataNode上。但是對每個DataNode而言,資料仍然可能存放在作業系統的Cache上,若存在至少一個正常運作的DataNode,則資料不會丢失。hsync成功傳回,則表示DFSClient DataQueue中的Packet不但成功發送到了3個DataNode,而且每個DataNode上的資料都持久化(sync)到了磁盤上,這樣就算所有的DataNode都重新開機,資料依然存在(hf?lush則沒法保證)。

在HBASE-19024之後,HBase 1.5.0以上的版本可以在服務端通過設定hbase.wal.hsync來選擇hf?lush或者hsync。低于1.5.0的版本,可以在表中設定DURABILITY屬性來實作。

在小米内部大部分HBase叢集上,綜合考慮寫入性能和資料可靠性兩方面因素,我們選擇使用預設的hf?lush來保證WAL持久性。因為底層的HDFS叢集已經保證了資料的三副本,并且每一個副本位于不同的機架上,而三個機架同時斷電的機率極小。但是對那些依賴雲服務的HBase叢集來說,有可能沒法保證副本落在不同機架,hsync是一個合理的選擇。

另外,針對小米廣告業務和雲服務這種對資料可靠性要求很高的業務,我們采用同步複制的方式來實作多個資料中心的資料備份,這樣雖然仍選擇用hf?lush,但資料已經被同步寫入兩個資料中心的6個DataNode上,同樣可以保證資料的高可靠性。

3.檔案讀取

HDFS檔案讀取流程如圖3-4所示。

帶你讀《HBase原理與實踐》之三:HBase依賴服務HBase依賴服務

圖3-4 HDFS檔案讀取流程

(1)讀取流程描述

1)DFSClient請求NameNode,擷取到對應read position的Block資訊(包括該Block落在了哪些DataNode上)。

2)DFSClient從Block對應的DataNode中選擇一個合适的DataNode,對選中的DataNode建立一個BlockReader以進行資料讀取。

HDFS讀取流程很簡單,但對HBase的讀取性能影響重大,尤其是Locality和短路讀這兩個最為核心的因素。

(2)Locality

某些服務可能和DataNode部署在同一批機器上。因為DataNode本身需要消耗的記憶體資源和CPU資源都非常少,主要消耗網絡帶寬和磁盤資源。而HBase的RegionServer服務本身是記憶體和CPU消耗型服務,于是我們把RegionServer和DataNode部署在一批機器上。對某個DFSClient來說,一個檔案在這台機器上的locality可以定義為:

locality =該檔案存儲在本地機器的位元組數之和 / 該檔案總位元組數

是以,locality是[0, 1]之間的一個數,locality越大,則讀取的資料都在本地,無需走網絡進行資料讀取,性能就越好。反之,則性能越差。

(3)短路讀(Short Circuit Read)

短路讀是指對那些Block落在和DFSClient同一台機器上的資料,可以不走TCP協定進行讀取,而是直接由DFSClient向本機的DataNode請求對應Block的檔案描述符(File Descriptor),然後建立一個BlockReaderLocal,通過fd進行資料讀取,這樣就節省了走本地TCP協定棧的開銷。

測試資料表明,locality和短路讀對HBase的讀性能影響重大。在locality=1.0情況下,不開短路讀的p99性能要比開短路讀差10%左右。如果用locality=0和locality=1相比,讀操作性能則差距巨大。

4. HDFS在HBase系統中扮演的角色

HBase使用HDFS存儲所有資料檔案,從HDFS的視角看,HBase就是它的用戶端。這樣的架構有幾點需要說明:

  • HBase本身并不存儲檔案,它隻規定檔案格式以及檔案内容,實際檔案存儲由HDFS實作。
  • HBase不提供機制保證存儲資料的高可靠,資料的高可靠性由HDFS的多副本機制保證。
  • HBase-HDFS體系是典型的計算存儲分離架構。這種輕耦合架構的好處是,一方面可以非常友善地使用其他存儲替代HDFS作為HBase的存儲方案;另一方面對于雲上服務來說,計算資源和存儲資源可以獨立擴容縮容,給雲上使用者帶來了極大的便利。

3.4 HBase在HDFS中的檔案布局

通過HDFS的用戶端列出HBase叢集的檔案如下:

hadoop@hbase37:~/hadoop-current/bin$ ./hdfs dfs -ls /hbase

Found 10 items

drwxr-xr-x - hadoop hadoop 0 2018-05-07 10:42 /hbase-nptest/.hbase-snapshot

drwxr-xr-x - hadoop hadoop 0 2018-04-27 14:04 /hbase-nptest/.tmp

drwxr-xr-x - hadoop hadoop 0 2018-07-06 21:07 /hbase-nptest/MasterProcWALs

drwxr-xr-x - hadoop hadoop 0 2018-06-25 17:14 /hbase-nptest/WALs

drwxr-xr-x - hadoop hadoop 0 2018-05-07 10:43 /hbase-nptest/archive

drwxr-xr-x - hadoop hadoop 0 2017-10-10 20:24 /hbase-nptest/corrupt

drwxr-xr-x - hadoop hadoop 0 2018-05-31 12:02 /hbase-nptest/data

-rw-r--r-- 3 hadoop hadoop 42 2017-09-29 17:30 /hbase-nptest/hbase.id

-rw-r--r-- 3 hadoop hadoop 7 2017-09-29 17:30 /hbase-nptest/hbase.version

drwxr-xr-x - hadoop hadoop 0 2018-07-06 21:22 /hbase-nptest/oldWALs

參數說明如下:

  • .hbase-snapshot?:snapshot檔案存儲目錄。使用者執行snapshot後,相關的snapshot中繼資料檔案存儲在該目錄。
  • .tmp:臨時檔案目錄,主要用于HBase表的建立和删除操作。表建立的時候首先會在tmp目錄下執行,執行成功後再将tmp目錄下的表資訊移動到實際表目錄下。表删除操作會将表目錄移動到tmp目錄下,一定時間過後再将tmp目錄下的檔案真正删除。
  • MasterProcWALs:存儲Master Procedure過程中的WAL檔案。Master Procedure功能主要用于可恢複的分布式DDL操作。在早期HBase版本中,分布式DDL操作一旦在執行到中間某個狀态發生當機等異常的情況時是沒有辦法復原的,這會導緻叢集中繼資料不一緻。Master Procedure功能使用WAL記錄DDL執行的中間狀态,在異常發生之後可以通過WAL回放明确定位到中間狀态點,繼續執行後續操作以保證整個DDL操作的完整性。
  • WALs:存儲叢集中所有RegionServer的HLog日志檔案。
  • archive:檔案歸檔目錄。這個目錄主要會在以下幾個場景下使用。

    所有對HFile檔案的删除操作都會将待删除檔案臨時放在該目錄。

進行Snapshot或者更新時使用到的歸檔目錄。

  • Compaction删除HFile的時候,也會把舊的HFile移動到這裡。
  • corrupt:存儲損壞的HLog檔案或者HFile檔案。
  • data:存儲叢集中所有Region的HFile資料。HFile檔案在data目錄下的完整路徑如下所示:

    /hbase/data/default/usertable/fa13562579a4c0ec84858f2c947e8723/family/105baeff31ed481cb708c65728965666

其中,default表示命名空間,usertable為表名,fa13562579a4c0ec84858f2c947e8723為Region名稱,family為列簇名,105baeff31ed481cb708c65728965666為HFile檔案名。

除了HFile檔案外,data目錄下還存儲了一些重要的子目錄和子檔案。

  • .tabledesc:表描述檔案,記錄對應表的基本schema資訊。
  • .tmp:表臨時目錄,主要用來存儲Flush和Compaction過程中的中間結果。以f?lush為例,MemStore中的KV資料落盤形成HFile首先會生成在.tmp目錄下,一旦完成再從.tmp目錄移動到對應的實際檔案目錄。
  • .regioninfo:Region描述檔案。
  • recovered.edits:存儲故障恢複時該Region需要回放的WAL日志資料。RegionServer當機之後,該節點上還沒有來得及f?lush到磁盤的資料需要通過WAL回放恢複,WAL檔案首先需要按照Region進行切分,每個Region擁有對應的WAL資料片段,回放時隻需要回放自己的WAL資料片段即可。
  • hbase.id:叢集啟動初始化的時候,建立的叢集唯一id。
  • hbase.version:HBase軟體版本檔案,代碼靜态版本。
  • oldWALs:WAL歸檔目錄。一旦一個WAL檔案中記錄的所有KV資料确認已經從MemStore持久化到HFile,那麼該WAL檔案就會被移到該目錄。

[1]嘗試動手搭建一個包含NameNode、DataNode、JournalNode、ZKFailvoerController的高可用HDFS叢集,并編寫操作文檔。

[2]請簡單闡述DFSClient在發現某個DataNode不可讀寫之後,該如何處理?

[3]在FileSystem.create()建立檔案時,可以通過如下接口指定檔案的Block大小:

帶你讀《HBase原理與實踐》之三:HBase依賴服務HBase依賴服務

其中,f表示待建立的檔案路徑;overwrite表示是否覆寫已存在的檔案;bufferSize表示寫入緩沖區大小;replication表示副本數;blockSize表示組成檔案的塊大小。請問如果Block設太大(例如1GB)會造成什麼不良影響?設太小會造成什麼不良影響?

[4]在第1章中,我們知道HBase的每一個RegionServer都會至少打開一個HLog檔案。事實上,HLog檔案是一直打開的,當有新的寫請求時,HBase會寫一個Entry到HLog檔案中,并執行hf?lush操作。如果RegionServer程序中途退出,那麼NameNode就無法知道這個檔案是否已經關閉,NameNode會認為這個檔案的寫入者仍然是原來的RegionServer程序。如果後面有其他程序嘗試去打開讀取這個未關閉的HLog檔案,則先執行fs.recoverLease()操作。請問recoverLease操作主要做了什麼事情?為什麼需要做recoverLease?

[5]在發現目前Block被寫滿的情況下,DFSClient會重新向NameNode申請Block嗎?NameNode配置設定Block的時候,有哪些政策?需要考慮哪些問題?

[6]程式設計題:在HBase的資料目錄中,經常會出現某個目錄下存放大量子目錄和檔案的情況。例如整個/hbase目錄下會有數萬個檔案(包括各層子目錄下的子檔案),現在請你設計一個程式,用來統計/hbase目錄下有多少個檔案,這些檔案的平均位元組數是多少。

提示:我們可以很友善地設計一個單線程遞歸程式來完成統計任務,為提高統計效率,請采用多線程來完成這個任務(HBASE-22867是一個典型的應用案例)。

拓展閱讀

[1]

https://hadoop.apache.org/docs/r1.2.1/hdfs_design.html

[2]HDFS小檔案存儲方案:

http://blog.cloudera.com/blog/2009/02/the-small-f?iles-problem/

[3]論文:Google GFS (

https://research.google.com/archive/gfs-sosp2003.pdf)

[4]論文:ZooKeeper: Wait-free coordination for Internet-scale systems (

https://www.usenix.org/legacy/event/atc10/tech/full_papers/Hunt.pdf)

[5]ZooKeeper Over:

https://zookeeper.apache.org/doc/r3.2.2/zookeeperOver.pdf

[6]ZooKeeper 官方文檔:

https://zookeeper.apache.org/doc/r3.3.4/index.html

繼續閱讀