1. 程式人生 > >深入解讀HBase2.0新功能之高可用讀Region Replica

深入解讀HBase2.0新功能之高可用讀Region Replica

數據庫

摘要: 基於時間線一致的高可用讀(Timeline-consistent High Available Reads),又稱Region replica,為HBase帶來了高可用讀的能力。本文主要介紹region replica這個功能設計的背景,技術細節和使用方法,同時會仔細分析這個功能的優缺點並給出使用建議。

前言

基於時間線一致的高可用讀(Timeline-consistent High Available Reads),又稱Region replica。其實早在HBase-1.2版本的時候,這個功能就已經開發完畢了,但是還是不太穩定,離生產可用級別還有一段距離,後來社區又陸陸續續修復了一些bug,比如說HBASE-18223。這些bug很多在HBase-1.4之後的版本才修復,也就是說region replica功能基本上在HBase-1.4之後才穩定下來。介於HBase-1.4版本目前實際生產中使用的還比較少,把region replica功能說成是HBase2.0中的新功能也不為過。

為什麽需要Region Replica

在CAP理論中,HBase一直是一個CP(Consistency&Partition tolerance)系統。HBase一直以來都在遵循著讀寫強一致的語義。所以說雖然在存儲層,HBase依賴HDFS實現了數據的多副本,但是在計算層,HBase的region只能在一臺RegionServer上線提供讀寫服務,來保持強一致。如果這臺服務器發生宕機時,Region需要從WAL中恢復還緩存在memstore中未刷寫成文件的數據,才能重新上線服務。
技術分享圖片

由於HBase的RegionServer是使用Zookeeper與Master保持lease。而為了不讓JVM GC停頓導致RegionServer被master“誤判”死亡,這個lease時間通常都會設置為20~30s,如果RegionServer使用的Heap比較大時,這個lease可能還會設的更長。加上宕機後,region需要re-assign,WAL可能需要 recoverlease和被replay操作,一個典型的region宕機恢復時間可能長達一分鐘!這就意味著在這一分鐘內,這個region都無法被讀寫。由於HBase是一個分布式系統,同一張表的數據可能分布在非常多的RegionServer和region裏。如果這是一個大HBase集群,有100臺RegionServer機器,那麽宕機一臺的話,可能只有1%的用戶數據被影響了。但是如果這是小用戶的HBase集群,一共就只有2臺RegionServer,宕機一臺意味著50%的用戶數據都在1~2分鐘之內無法服務,這是很多用戶都無法忍受的。

其實,很大一部分用戶對讀可用性的需求,可能比讀強一致的需求還要高。在故障場景下,只要保證讀繼續可用,“stale read”,即讀到之前的數據也可以接受。這就是為什麽我們需要read replica這個功能。

Region Replica技術細節

Region replica的本質,就是讓同一個region host在多個regionserver上。原來的region,稱為Default Replica(主region),提供了與之前類似的強一致讀寫體驗。而與此同時,根據配置的多少,會有一個或者多個region的副本,統稱為 region replica,在另外的RegionServer上被打開。並且由Master中的LoadBalancer來保證region和他們的副本,不會在同一個RegionServer打開,防止一臺服務器的宕機導致多個副本同時掛掉。

技術分享圖片

Region Replica的設計巧妙之處在於,額外的region副本並不意味著數據又會多出幾個副本。這些region replica在RegionServer上open時,使用的是和主region相同的HDFS目錄。也就是說主region裏有多少HFile,那麽在region replica中,這些數據都是可見的,都是可以讀出來的。
region replica相對於主region,有一些明顯的不同。
首先,region replica是不可寫的。這其實很容易理解,如果region replica也可以寫的話,那麽同一個region會在多個regionserver上被寫入,連主region上的強一致讀寫都沒法保證了。
再次,region replica是不能被split和merge的。region replica是主region的附屬品,任何發向region replica的split和merge請求都會被拒絕掉。只有當主region split/merge時,才會把這些region replica從meta表中刪掉,建立新生成region的region的replica。

replica之間的數據同步

那麽,既然region replica不能接受寫,它打開之後,怎麽讓新寫入的數據變的可見呢?這裏,region replica有兩種更新數據的方案:

1. 定期的StoreFile Refresher

這個方案非常好理解,region replica定期檢查一下它自己對應的HDFS目錄,如果發現文件有變動,比如說flush下來新的文件,文件被compaction掉,它就刷新一下自己的文件列表,這個過程非常像compaction完成之後刪除被compact掉的文件和加入新的文件的流程。StoreFile Refresher方案非常簡單,只需要在RegionServer中起一個定時執行的Chroe,定期去檢查一下它上面的region哪些是region replica,哪些到了設置好的刷新周期,然後刷新就可以了。但這個方案缺點也十分明顯,主region寫入的數據,只有當flush下來後,才能被region replica看到。而且storeFile Refresher本身還有一個刷新的周期,設的太短了,list文件列表對NN的沖擊太頻繁,設的太長,就會造成數據長時間在region replica中都不可見

2. Internal Replication

我們知道,HBase是有replication鏈路的,支持把一個HBase集群的數據通過replication復制到另外一個集群。那麽,同樣的原理,可以在HBase集群內部建立一條replication通道,把一個Server上的主region的數據,復制到另一個Server的region replica上。那麽region replica接收到這些數據之後,會把他們寫入memstore中。對,你沒看錯,剛才我說了region replica是不接受寫的,這是指replica不接受來自客戶端的寫,如果來自主region的replication的數據,它還是會寫入memstore的。但是,這個寫和普通的寫有很明顯的區別。第一個,replica region在寫入來自主region的時候,是不寫WAL的,因為這些數據已經在主region所在的WAL中持久化了,replica中無需再次落盤。第二個,replica region的memstore中的數據是不會被flush成HFile。我們知道,HBase的replication是基於復制WAL文件實現的,那麽在主region進行flush時,也會寫入特殊的標記Flush Marker。當region replica收到這樣的標記時,就直接會把所有memstore裏的數據丟掉,再做一次HDFS目錄的刷新,把主region剛剛刷下去的那個HFile include進來。同樣,如果主region發生了compaction,也會寫入相應的Compaction Marker。讀到這樣的標記後,replica region也會做類似的動作。

Internal replication加快了數據在region replica中的可見速度。通過replication方案,只要replication本身不發生阻塞和延遲,region replica中的數據可以做到和主region只差幾百ms。但是,replication方案本身也存在幾個問題:

  • META表 無法通過replication來同步數據
    如果給meta表開了region replica功能,meta表主region和replica之間的數據同步,只能通過定期的StoreFile Refresher機制。因為HBase的replication機制中會過濾掉meta表的數據。

  • 需要消耗額外的CPU和網絡帶寬來做Replication
    由於region replica的數據同步需要,需要在HBase集群內部建立replication通道,而且有幾個replica,就意味著需要從主region發送幾份數據。這會增加RegionServer的CPU使用,同時在server之間復制數據還需要占用帶寬

  • 寫memstore需要額外的內存開銷
    為了讓replica region的數據缺失的內容盡量的少,主region的數據會通過replication發送到replica中,這些數據都會保存在memstore中。也就是說同樣的一份數據,會同時存在主region的memstore中,也會存在replica region的memstore中。replica的數量是幾,那麽memstore的內存使用量就是幾倍。

下面的兩個問題雖然可以通過配置一些參數解決,但是列在這裏,仍然需要註意,因為一旦參數沒有配對,就會產生這樣的問題。

  • 在replica region failover後,讀到的數據可能會回退
    我們假設一個情況。客戶端寫入X=1,主region發生flush,X=1刷在了HFile中,然後客戶端繼續寫入X=2,X=3,那麽在主region的memstore中X=3。同時,通過replication,X=2,X=3也被復制到了replica region的memstore中。如果客戶端去replica中去讀取X的數據,也能讀到3。但是由於replica region memstore中的數據是不寫WAL的,也不刷盤。那麽當replica所在的機器宕機後,它是沒有任何數據恢復流程的,他會直接在其他RegionServer上線。上線後它只能讀取HFile,無法感知主region memstore裏的數據。這時如果客戶端來replica上讀取數據,那麽他只會讀到HFile中的X=1。也就是說之前客戶端可以讀到X=3,但後來卻只能讀到X=1了,數據出現了回退。為了避免出現這樣的問題,可以配置一個hbase.region.replica.wait.for.primary.flush=true的參數,配置之後,replica region上線後,會被標記為不可讀,同時它會去觸發一次主region的flush操作。只有收到主region的flush marker之後,replica才把自己標記為可讀,防止讀回退

  • replica memstore過大導致寫阻塞
    上面說過,replica的region中memstore是不會主動flush的,只有收到主region的flush操作,才會去flush。同一臺RegionServer上可能有一些region replica和其他的主region同時存在。這些replica可能由於復制延遲(沒有收到flush marker),或者主region沒有發生flush,導致一直占用內存不釋放。這會造成整體的內存超過水位線,導致正常的寫入被阻塞。為了防止這個問題的出現,HBase中有一個參數叫做hbase.region.replica.storefile.refresh.memstore.multiplier,默認值是4。這個參數的意思是說,如果最大的replica region的memstore已經超過了最大的主region memstore的內存的4倍,就主動觸發一次StoreFile Refresher去更新文件列表,如果確實發生了flush,那麽replica內存裏的數據就能被釋放掉。但是,這只是解決了replication延遲導致的未flush問題,如果這個replica的主region確實沒有flush過,內存還是不能被釋放。寫入阻塞還是會存在

Timeline Consistency Read

無論是StoreFile Refresher還是Internal replication,主region和replica之間的數據更新都是異步的,這就導致在replica region中讀取數據時,都不是強一致的。read replica的作者把從region replica中讀數據的一致性等級定為Timeline Consistency。只有用戶明確表示能夠接受Timeline consistency,客戶端的請求才會發往replica中。
技術分享圖片

比如說上圖中,如果客戶端是需要強一致讀,那麽客戶端的請求只會發往主region,即replica_id=0的region,他就會讀到X=3.如果他選擇了Timeline consistency讀,那麽根據配置,他的讀可能落在主上,那麽他仍然會讀到X=3,如果他的讀落在了replica_id=1的region上,因為復制延遲的存在,他就只能讀到X=2.如果落在了replica_id=2上,由於replication鏈路出現了問題,他就只能讀到X=1。

Region replica的使用方法

服務端配置

hbase.regionserver.storefile.refresh.period

如果要使用StoreFile Refresher來做為Region replica之間同步數據的策略,就必須把這個值設置為一個大於0的數,即刷新storefile的間隔周期(單位為ms)上面的章節講過,這個值要不能太大,也不能太小。

hbase.regionserver.meta.storefile.refresh.period

由於Meta表的region replica不能通過replication來同步,所以如果要開啟meta表的region replica,必須把這個參數設成一個不為0的值,具體作用參見上一個參數,這個參數只對meta表生效。

hbase.region.replica.replication.enabled
hbase.region.replica.replication.memstore.enabled

如果要使用Internal replication的方式在Region replica之間同步數據的策略,必須把這兩個參數都設置為true

hbase.master.hfilecleaner.ttl

在主region發生compaction之後,被compact掉的文件會放入Achieve文件夾內,超過hbase.master.hfilecleaner.ttl時間後,文件就會被從HDFS刪除掉。而此時,可能replica region正在讀取這個文件,這會造成用戶的讀取拋錯返回。如果不想要這種情況發生,就可以把這個參數設為一個很大的值,比如說3600000(一小時),總沒有讀操作需要讀一個小時了吧?

hbase.meta.replica.count

mata表的replica份數,默認為1,即不開啟meta表的replica。如果想讓meta表有額外的一個replica,就可以把這個值設為2,依次類推。此參數只影響meta表的replica份數。用戶表的replica份數是在表級別配置的,這個我後面會講

hbase.region.replica.storefile.refresh.memstore.multiplier

這個參數我在上面的章節裏有講,默認為4

hbase.region.replica.wait.for.primary.flush

這個參數我在上面的章節裏有講,默認為true


需要註意的是,開啟region replica之後,Master的balancer一定要用默認的StochasticLoadBalancer,只有這個balancer會盡量使主region和他的replica不在同一臺機器上。其他的balaner會無區別對待所有的region。

客戶端配置

hbase.ipc.client.specificThreadForWriting

因為當存在region replica時,當客戶端發往主region的請求超時後,會發起一個請求到replica region,當其中一個請求放回後,就無需再等待另一個請求的結果了,通常要中斷這個請求,使用專門的的線程來發送請求,比較容易處理中斷。所以如果要使用region replica,這個參數要配為true。

hbase.client.primaryCallTimeout.get
hbase.client.primaryCallTimeout.multiget
hbase.client.replicaCallTimeout.scan

分別對應著,get、multiget、scan時等待主region返回結果的時間。如果把這個值設為1000ms,那麽客戶端的請求在發往主region超過1000ms還沒返回後,就會再發一個請求到replica region(如果有多個replica的話,就會同時發往多個replica)

hbase.meta.replicas.use

如果服務端上開啟了meta表的replica後,客戶端可以使用這個參數來控制是否使用meta表的replica的region。

建表

在shell建表時,只需在表的屬性裏加上REGION_REPLICATION => xx就可以了,如

create 't1', 'f1', {REGION_REPLICATION => 2}

Replica的份數支持動態修改,但修改之前必須disable表

diable 't1'
alter 't1', {REGION_REPLICATION => 1}
enable 't1'

訪問有replica的表

如果可以按請求設置一致性級別,如果把請求的一致性級別設為Consistency.TIMELINE,即有可能讀到replica上

Get get1 = new Get(row);
get1.setConsistency(Consistency.TIMELINE);
...
ArrayList<Get> gets = new ArrayList<Get>();
gets.add(get1);
...
Result[] results = table.get(gets);

另外,用戶可以通過Result.isStale()方法來獲得返回的result是否來自主region,如果為isStale為false,則結果來自主region。

Result result = table.get(get);
if (result.isStale()) {
...
}

總結和建議

Region Replica功能給HBase用戶帶來了高可用的讀能力,提高了HBase的可用性,但同時也存在一定的缺點:

  • 高可用的讀基於Timeline consistency,用戶需要接受非強一致性讀才能開啟這個功能

  • 使用Replication來做數據同步意味著額外的CPU,帶寬消耗,同時根據replica的多少,可能會有數倍的memstore內存消耗

  • 讀取replica region中的block同樣會進block cache(如果表開啟了block cache的話),這意味著數倍的cache開銷

  • 客戶端Timeline consistency讀可能會把請求發往多個replica,可能帶來更多的網絡開銷

Region Replica只帶來了高可用的讀,宕機情況下的寫,仍然取決於主region的恢復時間,因此MTTR時間並沒有隨著使用Region replica而改善。雖然說region replica的作者在規劃中有寫計劃在宕機時把一個replica提升為主,來優化MTTR時間,但截至目前為止,還沒有實現。

個人建議,region replica功能適合於用戶集群規模較小,對讀可用性非常在意,同時又可以接受非強一致性讀的情況下開啟。如果集群規模較大,或者讀寫流量非常大的集群上開啟此功能,需要留意內存使用和網絡帶寬。Memstore占用內存過高可能會導致region頻繁刷盤,影響寫性能,同時cache容量的翻倍會導致一部分讀請求擊穿cache直接落盤,導致讀性能的下降。

雲端使用

阿裏HBase目前已經在阿裏雲提供商業化服務,任何有需求的用戶都可以在阿裏雲端使用深入改進的、一站式的HBase服務。雲HBase版本與自建HBase相比在運維、可靠性、性能、穩定性、安全、成本等方面均有很多的改進,更多內容歡迎大家關註 https://www.aliyun.com/product/hbase
同時,雲HBase2.0 在2018年6月6日將正式發布,點擊了解更多: https://promotion.aliyun.com/ntms/act/hbase20.html

原文鏈接


深入解讀HBase2.0新功能之高可用讀Region Replica