1. 程式人生 > 實用技巧 >HBase——Replication(複製)原理

HBase——Replication(複製)原理

前言

Replication:複製,指的是持續的將同一份資料拷貝到多個地方進行儲存,是各種儲存系統中常見而又重要的一個概念,

可以指資料庫中主庫和從庫的複製,也可以指分散式叢集中多個叢集之間的複製,還可以指分散式系統中多個副本之間的複製。

它的難點在於資料通常是不斷變化的,需要持續的將變化也反映到多個數據拷貝上,並保證這些拷貝是完全一致的。

通常來說,資料複製到多個拷貝上有如下好處:

  • 多個備份提高了資料的可靠性

  • 通過主從資料庫/主備叢集之間的複製,來分離OLTP和OLAP請求

  • 提高可用性,即使在單副本掛掉的情況下,依然可以有其他副本來提供讀寫服務

  • 可擴充套件,通過增加副本來服務更多的讀寫請求

  • 跨地域資料中心之間的複製,Client通過讀寫最近的資料中心來降低請求延遲

HBase中的Replication指的是主備叢集間的複製,用於將主叢集的寫入記錄複製到備叢集。HBase目前共支援3種Replication:

  • 非同步Replication
  • 序列Replication
  • 同步Replication

一、HBase架構

如果想把HBase的Replication搞清楚,首先需要了解下HBase的架構。

HBase叢集是由一組程序組成的,程序按角色分為Master和RegionServer,其中Master負責DDL操作,比如建表、刪表,而RegionServer負責DML操作,比如資料的讀寫操作等。

從資料檢視上講,HBase中的Table會按Range切分為多個Region,然後由不同的RegionServer來負責對外提供服務。

RegionServer的內部則主要有BlockCache,MemStore和WAL等幾部分組成。

需要注意的是每個Region的每個Column Family有自己獨享的MemStore,但是BlockCache和WAL則是多個Region共享的。

WAL(Write-ahead logging)是資料庫中的常用技術,所有的修改在寫入資料庫之前都需要持久化到WAL中,從而確保了在出現故障的時候,可以從WAL中回放出已經成功寫入的資料。

二、非同步Replication

HBase中的Replication也是基於WAL的,其在主叢集的每個RegionServer程序內部起了一個叫做ReplicationSource的執行緒來負責Replication,

同時在備叢集的每個RegionServer內部起了一個ReplicationSink的執行緒來負責接收Replication資料。

ReplicationSource記錄需要同步的WAL佇列,然後不斷讀取WAL中的內容,同時可以根據Replication的配置做一些過濾,比如是否要複製這個表的資料等,

然後通過replicateWALEntry這個Rpc呼叫來發送給備叢集的RegionServer,備叢集的ReplicationSink執行緒則負責將收到的資料轉換為put/delete操作,以batch的形式寫入到備叢集中。

因為是後臺執行緒非同步的讀取WAL並複製到備叢集,所以這種Replication方式叫做非同步Replication,正常情況下備叢集收到最新寫入資料的延遲在秒級別。

三、序列Replication

注意:此功能是HBase 2.1中引入的。增加屬性:SERIAL => true

序列Replication指的是:對於某個Region來說,嚴格按照主叢集的寫入順序複製到備叢集,其是一種特殊的Replication。

同時預設的非同步Replication不是序列的,主要原因是Region是可以移動的,比如HBase在進行負載均衡時移動Region。

假設RegionA首先在RegionServer1上,然後其被移動到了RegionServer2上,由於非同步Replication是存在延遲的,

所以RegionA的最後一部分寫入記錄還沒有完全複製到備叢集上。在Region移動到RegionServer2之後,其開始接收新的寫入請求,並由RegionServer2來複制到備叢集,

所以在這個時候RegionServer1和RegionServer2會同時向備叢集進行復制,而且寫入記錄複製到備叢集的順序是不確定的。

如上圖所示這種極端情況下,還會導致主備叢集資料的不一致。比如RegionServer1上最後一個未同步的寫入操作是Put,

而RegionA被移動到RegionServer2上的第一個寫入操作是Delete,在主叢集上其寫入順序是先Put後Delete,

如果RegionServer2上的Delete操作先被複制到了備叢集,然後備叢集做了一次Major compaction,其會刪除掉這個Delete marker,然後Put操作才被同步到了備叢集,

因為Delete已經被Major compact掉了,這個Put將永遠無法被刪除,所以備叢集的資料將會比主叢集多。

解決這個問題的關鍵在於需要確保RegionServer2上的新寫入操作必須在RegionServer1上的寫入操作複製完成之後再進行復制。

所以序列Replication引入了一個叫做Barrier的概念,每當Region open的時候,就會寫入一個新的Barrier,其值是Region open時讀到的最大SequenceId加1。

SequenceId是HBase中的一個重要概念,每個Region都有一個SequenceId,其隨著資料寫入嚴格遞增,同時SequenceId會隨著每次寫入操作一起寫入到WAL中。

所以當Region移動的時候,Region會在新的RegionServer重新開啟,這時就會寫入一個新的Barrier,Region被移動多次之後,就會寫入多個Barrier,來將Region的寫入操作劃分成為多個區間。

同時每個Region都維護了一個lastPushedSequenceId,其代表這個Region當前推送成功的最後一個寫操作的SequenceId,

這樣就可以根據Barrier列表和lastPushedSequenceId來判斷WAL中的一個寫入操作是否能夠複製到備叢集了。

以上圖為例,Pending的寫入記錄就需要等待lastPushedSequenceId推到Barrier2之後才能開始複製。

由於每個區間之間只會有一個RegionServer來負責複製,所以只有和lastPushedSequenceId在同一個區間內的RegionServer才能進行復制,

並在複製成功後不斷更新lastPushedSequenceId,而在lastPushedSequenceId之後各個區間的RegionServer則需要等待lastPushedSequenceId被推到自己區間的起始Barrier,

然後才能開始複製,從而確保了Region的寫入操作可以嚴格按照主叢集的寫入順序序列的複製到備叢集。

# 串行復制鏈路
add_peer '1', CLUSTER_KEY => "server1.cie.com:2181:/hbase", SERIAL => true

# 設定為false,不可用
set_peer_serial '1',false

# 設定為true,可用
set_peer_serial '1', true

四、同步Replication

同步Replication是和非同步Replication對稱的概念,其指的是主叢集的寫入操作必須被同步的寫入到備叢集中。

非同步Replication的最大問題在於複製是存在延遲的,所以在主叢集整叢集掛掉的情況下,備叢集是沒有已經寫入的完整資料的,

對於一致性要求較高的業務來說,是不能把讀寫完全切到備叢集的,因為在這個時候可能存在部分最近寫入的資料無法從備叢集讀到。

所以同步Replication的核心思路就是在寫入主叢集WAL的同時,在備叢集上寫入一份RemoteWAL,只有同時在主叢集的WAL和備叢集的RemoteWAL寫入成功了,才會返回給Client說寫入成功。

這樣當主叢集掛掉的時候,便可以在備叢集上根據Remote WAL來回放出來主叢集上所有寫入記錄,從而確保備叢集和主叢集資料的一致。

需要注意的是,同步Replication是在非同步Replication的基礎之上的,也就是說非同步Replication的複製鏈路還會繼續保留,同時增加了新的寫Remote WAL的步驟。

對於具體的實現細節來說,首先是增加了一個Sync replication state的概念,其總共有三個狀態, 分別是Active,Downgrade Active和Standby。

這幾個狀態的轉換關係如下圖所示,Standby在提主的時候需要首先提升為Downgrade Active,然後才能提升為Active。但是Active是可以直接降級為Standby的。

目前這個狀態是儲存在ReplicationPeerConfig中的, 其表示一個叢集在這個ReplicationPeer中處於哪個狀態。

然後實現了一個DualAsyncFSWAL來同時寫主叢集的WAL和備份叢集的Remote WAL。寫WAL的操作是對於HDFS的rpc請求,其會有三種結果:成功,失敗或者超時。

當超時的時候,對於HBase來說結果是不確定的,即資料有可能成功寫入到WAL或Remote WAL裡了,也有可能沒有。只有同時寫成功或者同時寫失敗的時候,

主叢集和備叢集才會有一樣的WAL,如果是主叢集寫WAL成功,寫Remote WAL失敗或者超時,這時候主叢集WAL裡的資料就有可能比備叢集的Remote WAL多。

相反如果寫備叢集Remote WAL成功了,而主叢集的WAL寫失敗或者超時了,備叢集的Remote WAL裡的資料就有可能比主叢集多。當兩邊都超時的時候, 就不確定那邊多了。

所以同步複製的關鍵就在於在上述情況下,如何確保主備叢集資料的最終一致。即在切換主備叢集的時候,Client應該始終從主備叢集看到一致的資料。

而且在主備沒有達到一致的中間狀態時,需要一些限制來確保Client沒法讀到這種中間不一致的結果。所以總結一下就是主備叢集最終一致,

但對於Client來說是強一致,即成功寫入的資料無論主備叢集都要一定能讀到。具體的實現細節可以參考HBaseCon Asia 2018:HBase at Xiaomi[1]。

對比非同步複製來看,同步複製主要是影響的寫路徑,從我們的測試結果上來看,大概會有14%的效能下降,後續計劃在HBASE-20422[2]中進行優化。

五、自定義Replication Endpoint

除了上述3種Replication之外,HBase還支援外掛式的Replication Endpoint,可以自定義Replication Endpoint來實現各種各樣的功能。

引用: