京東JDHBase異地多活實踐

  • 2020 年 3 月 11 日
  • 筆記

JDHBase在京東集團作為線上kv存儲,承擔了大量在線業務,11.11、6.18 均經歷了每天萬億級讀寫訪問請求,目前規模達到7000+節點,存儲容量達到了90PB。場景涉及商品訂單、評價、用戶畫像、個性推薦、金融風控、物流、監控等700+業務。

JDHBase上承載了大量核心業務,遍布全球多個Data Center。為了保障業務穩定不間斷運行,我們構建了JDHBase集群的異地多活系統。主要介紹在我們在異地多活系統的實踐。

HBase Replication原理

HBase是典型的LSM(Log-Structured Merge-Tree)結構資料庫,服務端響應客戶端寫請求過程中,會寫入Memstore(記憶體)中和順序的寫入WAL日誌(HDFS文件)中,WAL日誌確保數據真正寫入磁碟,從而在節點故障時恢復未被持久化的記憶體數據。

HBase的Replication是基於WAL日誌文件的。在主集群中的每個RegionServer上,由ReplicationSource執行緒來負責推送數據,在備集群的RegionServer上由ReplicationSink執行緒負責接收數據。ReplicationSource不斷讀取WAL日誌文件的數據,根據Replication的配置做一些過濾,然後通過replicateWALEntry的rpc調用來發送給備集群的RegionServer,備集群的ReplicationSink執行緒則負責將收到的數據轉換為put/delete操作,以batch的形式寫入到備集群中。

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

JDHBase異地多活架構

JDHBase服務端與客戶端交互主要包含三個組件:Client、JDHBase集群、Fox Manager。

Client啟動時首先向Fox Manager端彙報用戶資訊,Fox Manager進行用戶認證後,返回集群連接資訊,Clinet收到集群連接資訊後,創建集群連接HConnection,從而與Fox Manager指定的集群進行數據交互。

1

Fox Manager配置中心

負責維護用戶及JDHBase集群資訊,為用戶提供配置服務,同時管理員做配置管理。

  • Policy Server:分散式無狀態的服務節點,響應外部請求。數據持久化目前為可選的Mysql或Zookeeper。Policy Server中還包含了一個可選的Rule Engine插件,用於根據規則和集群的狀態,自動修改用戶配置,如集群連接地址資訊、客戶端參數等。
  • Service Center:Admin配置中心的UI介面,供管理員使用。
  • VIP Load Balance:對外將一組Policy Server提供統一訪問地址並提供負載均衡能力。

2

JDHBase Cluster

JDHBase Cluster提供高吞吐的在線OLTP能力。我們對可靠性要求比較高的業務做了異地多活備份。

  • Active Cluster:正常情況下業務運行在此集群上。數據會非同步備份到Standby Cluster,同時保證數據不丟失,但是會有延遲。
  • Standby Cluster:異常情況下,全部或部分業務會切換到此集群運行。在此集群上運行的業務數據也會非同步備份到Active Cluster上。

兩個集群間通過Replication備份數據,根據集群ID防止數據迴環。主備集群間數據達到最終一致性。

實際生產中,我們根據兩個建群間的Replication,構建了多集群間的Replication拓撲,使得集群互為主備。一個集群上會承載多個業務,不同的業務的備份也會散落在不同的集群上,形成多集群間的拓撲結構。

3

Client

Client負責拉取Fox Manager端配置資訊,根據配置資訊為用戶提供介面,與主集群或者備集群進行數據交互,同時將客戶端狀態上報給Fox Manager端。

集群切換

HBase在讀寫數據時,需要先經過數據路由,找到數據所在(或應當所在)的節點位置,然後與節點進行數據交互。簡單來說包含以下三步:

1、client端訪問HBase集群的zookeeper地址,通過訪問znode獲取集群META表所在位置。

2、訪問META表所在節點,查詢META表獲取數據分片(Region)資訊。同時快取META表數據。

3、根據數據分片資訊訪問數據所在節點,進行數據交互。

JDHBase在client端數據路由前,多加了一步訪問Fox Manager的步驟,這一步驟主要有兩個作用:一是進行用戶認證;二是獲取用戶集群資訊;三是獲取客戶端參數。

對集群切換來說,重要的是用戶集群資訊和客戶端參數。Client端拿到具體的集群資訊(zk地址),然後進行正常的數據路由,這樣業務的client端不需要關心訪問哪個集群,Fox Manager端只要保證為client提供的路由集群可用即可。

Fox Manager還會為Client提供一些特殊配置參數,例如重試、超時等,這些配置參數依據兩個維度:集群特性和業務屬性。這些參數的設置需要結合業務場景和要求長期觀察,屬於專家經驗;也包括一些極端情況下的臨時參數。

我們也在client sdk中添加了metrics,用於評估client端視角的服務可用性。基於metrics,我們為一些極度敏感的業務開啟客戶端切換,當客戶端可用率降低生效。

在client sdk中添加的metrics,用於評估client端視角的服務可用性。Client啟動後會與Fox Manager建立心跳,一方面通過心跳上報客戶端狀態以及部分metrics指標到Fox Manager,這些數據能夠幫助我們分析服務運行狀態;另一方面Client端能夠獲取Fox Manager端對Client的配置更新。這樣,當管理員在Fox Manager為Client更新了集群配置,Client端能夠及時感知並重建數據路由。

另外,我們也做了對Client的精準控制。一方面可以使業務的部分Client實例路由到不同集群,另一方面可以作為一些極端情況下單個Client實例強制更新集群資訊並切換的備用手段。

自動切換

在有了主備集群切換之後,我們仍面臨時效性的問題。故障情況下,我們從監控到異常到報警,到人工介入,最快仍需要分鐘級恢復服務可用性。這對一些線上業務來說仍然不可接受。

為了提高服務SLA品質,我們開發了基於策略的主備集群自動切換。可以根據策略在服務異常時,觸發切換,將故障恢復時間控制到秒級。

首先我們在HMaster上做了狀態檢測插件,用於收集一些影響服務可用性的指標資訊,heartbeat的方式上報到Fox Manager的PolicyServer中。

PolicyServer 是對外提供查詢和修改策略的服務,它所有策略數據會存儲在MySQL中。可以通過加節點的方式動態擴展形成一個服務集群,避免單點問題。

PolicyServer中的Rule Engine負責根據HMaster上報的集群狀態的指標資訊推測執行切換策略。服務可用性對不同指標的敏感度不同,本質上Rule Engine在多個時間窗口上對不同的指標或多個指標的組合執行策略。

Rule Engine不需要高吞吐,重要的是保障可用性,因此基於Raft做了高可用。Active的Rule Engine節點掛掉後,立即會被另外一台節點接管。

動態參數&自動調速

Replication本身是通過RegionServer發送到備機群,而RegionServer本身有大量執行緒用於客戶端請求,Replication Source的執行緒和負載很難與客戶端請求相匹配,在大量寫或者有熱點的情況下,很容易出現Replication積壓。

這個問題我們可以通過調節Replication 參數來緩解這種積壓的情況。HBase本身基於觀察者模式支援動態參數,更新RegionServer節點參數後,執行update config動作即可生效。我們擴展了動態參數,將Replication的一些參數做成了動態生效的。當Replication積壓比較嚴重時,可以在集群上或者在響應的分組、節點調整參數,不需要重啟節點。

雖然Replication動態參數不需要重啟RegionServer,但是上線還是比較麻煩的,需要人工參與,並且寫熱點積壓不可預測,依然很難做到Replication平穩順滑。因此我們進一步在Replication Source端根據當前節點積壓的情況(幾個閾值),在一定範圍內自動調節Replication參數,從而達到自動調速的功能。目前參數自動調節範圍在基礎參數值的1-2倍之間。

跨機房異地數據中心的之間的頻寬是有限的,在業務流量高峰期不能將有限的網路資源用於同步數據。因此在Fox Manager端我們也做了對集群的相應控制,分時段調整Replication速度。

串列Repication

主備集群間的Replication本身是非同步的,正常情況下兩個集群可以達到最終一致性。但是某些情況下並不能完全保證。

在HBase的Replication中,通過讀取每個RegionServer中的WAL將數據變化推到備集群。HBase在zookeeper中維護了一個對WAL文件的隊列,因此可以按創建時間順序讀取這些WAL文件。但是當Region發生移動或者RegionServer故障轉移,那麼Region所在的新的RegionServer上的WAL日誌可能會先於老的WAL日誌推送到備集群,這種情況下備集群上的數據寫入順序與主集群是不一致的。更極端的情況,如果這種順序不一致發生在同一條數據上,那麼可能會導致數據永久不一致。

舉個例子,首先在主集群中執行Put,然後執行Delete來刪除它,但是Delete操作先replication到了備集群,而備集群如果在接收Put之前進行了major compact,major compact過程會刪除掉delete marker,隨後備集群接收到了這條put,那麼這條put在備集群上將沒有機會再delete,將會一直存在。

解決這個問題需要保證任何情況下,Replciation的順序與主集群的mutation順序是一致的,即串列Replication(Serial Replication, backport form v2.1)。例如當Region發生移動從RegionServer1移動到了RegionServer2,那麼RegionServer2應當等待RegionServer1將此region的所有數據推送完,再進行推送。

串列Replciation使用Barrier和lastPushedSequenceId來解決這個問題。每當Region發生Open時,都會在meta表中記錄一個新的Barrier,這個Barrier為當前Region的最大SequenceId + 1。lastPushedSequenceId為當前region推送到備集群的SequenceId,在Replciation的過程中,每個batch成功,會在Zookeeper中記錄最大的SequenceId,即lastPushedSequenceId。

如圖所示,一個Region從RegionServer1移動到RegionServer2,又到RegionServer3,發生多次Region Open,記錄了多個Barrier,構成多個Range:[ Barrier(n) , Barrier(n+1) )。期間有多個mutation操作記錄的SequenceId:s1、s2、s3、……

RegionServer在進行數據Replication前,首先檢查lastPushedSequenceId 是否大於自己區間的起始Barrier。例如上圖中RegionServer3會首先檢查,當lastPushedSequenceId >= Barrier1 – 1時才會進行Replication,而此lastPushedSequenceId = s2,則說明lastPushedSequenceId所在Range的RegionServer2正在進行Replication,那麼RegionServer3需要等待。這樣就保證了數據抵達備集群的順序與主集群的寫入順序是相同的。

總結與展望

JDHBase在不斷吸收業界異地災備經驗的同時,也經過一系列的實踐和演進,目前SLA已經能夠達到99.98%,從毫無異地容災措施到完善的監控、告警、切換、一致性保障機制,為業務提供穩定可靠的存儲服務。

同時隨著業務的增多和數據量的增大,集群規模也越來越大,仍面臨一些挑戰,未來我們在異地災備方面將會著力在同步的Replication、去zookeeper依賴、客戶端視角自動切換、降低數據冗餘等方面繼續提升可靠性及穩定性。

References

1、https://hbase.apache.org/book.html#_cluster_replication

2、https://mapr.com/blog/in-depth-look-hbase-architecture/

3、hhttps://issues.apache.org/jira/browse/HBASE-20360

4、https://issues.apache.org/jira/browse/HBASE-20046