做者:管延信git
云溪數據庫 ZNBase 是由浪潮開源的一款 NewSQL 分佈式數據庫,具有 HTAP 特性,擁有強一致、高可用的分佈式架構。對於一個高可用的分佈式系統來講,爲了保障不一樣集羣不一樣節點的數據一致,一致性算法尤其重要。Raft 是一種管理日誌複製的分佈式一致性算法,包括 ZNBase 在內的不少分佈式系統都採用 Raft 做爲底層的一致性協議。本系列文章將爲你們介紹 Raft 一致性算法在 ZNBase 中的落地實踐,並深刻解析 ZNBase 技術團隊根據自身業務需求對 Raft 協議作出的五大優化改進。算法
Raft 簡介
Raft 做爲一種管理日誌複製的分佈式一致性算法,由斯坦福大學的 Diego Ongaro 和 John Ousterhout 在論文中提出。在 Raft 出現以前,Paxos 一直是分佈式一致性算法的標準。但 Paxos 相對難以理解,Raft 的設計目標就是簡化 Paxos,使得一致性算法更容易理解和實現。數據庫
Paxos 和 Raft 都是分佈式一致性算法,其過程如同投票選舉領袖(Leader),參選者(Candidate)須要說服大多數投票者(Follower)給他投票,一旦選舉出領袖,就由領袖發號施令。Paxos 和 Raft 的區別在於選舉的具體過程不一樣,社區中關於 Raft 算法的詳細講解很是豐富,這裏就再也不贅述。查看詳情安全
ZNBase 中的 Raft 算法
云溪數據庫 —— ZNBase 是分佈式數據庫,與 OceanBase、CockroachDB、TiDB 同樣都是NewSQL 家族的一員。云溪數據庫具有強一致、高可用的分佈式架構,可以水平擴展,提供企業級的安全特性,徹底兼容 PostgreSQL 協議,可以爲用戶提供完整的分佈式數據庫解決方案。ZNBase 的總體架構如圖 1 所示:架構
圖1:ZNBase 的總體架構負載均衡
ZNBase 各方面的強一致性都經過 Raft 算法實現。首先,Raft 算法保證分佈式多副本之間數據強一致性以及外部讀寫的一致性。簡而言之,ZNBase 中數據會有多個副本,這些副本存放在不一樣的機器上,當其中一臺機器故障宕機後,數據庫依舊可以對外提供服務。此外,ZNBase 會根據插入數據的鍵,將數據劃分爲多個 Range,每一個 Range 上的數據均由一個 Raft Group 來維持多個副本之間數據的一致性。所以,準確地說 ZNBase 使用的是 Multi-Raft 算法。分佈式
具體來講,ZNBase 的存儲層基於 RocksDB 開發,利用單機的 RocksDB,ZNBase 能夠將數據快速地存儲在磁盤上;在出現單機故障時,利用 Raft 算法能夠快速地將數據複製到機器上。在這個過程當中,數據的寫入是經過 Raft 算法接口實現的,而不是直接寫入 RocksDB。經過 Raft 算法,ZNBase 變成了一個分佈式的鍵值存儲系統,面對不超過集羣半數的機器故障狀況宕機,徹底可以經過 Raft 算法自動把副本補全,作到業務對故障的無感知。 性能
在項目開發前期,ZNBase 中的 Raft 算法採用的是開源的 etcd-raft 模塊,該模塊主要提供以下幾點功能:優化
- Leader 選舉;
- 成員變動,能夠細分爲:增長節點、刪除節點、Leader 轉移等;
- 日誌複製。
ZNBase 利用 etcd-raft 模塊進行數據複製,每條數據操做都最終轉化成一條 RaftLog,經過 RaftLog 複製功能,將數據操做安全可靠地同步到 Raft Group 中的每個節點上。不過在實際操做中,根據 Raft 的協議,只須要同步複製到多數節點,便可安全地認爲數據寫入成功。 url
可是在後續的生產實踐中,ZNBase 研發團隊逐漸發現 etcd-raft 的模塊仍存在諸多限制,因而陸續開展了以下多個方面的優化工做,具體包括:
- 新增 Raft 角色
- 新增 Leader 親和選舉
- 混合序列化
- Raft Log 分離與定製存儲
- Raft 心跳與數據分離
下文將着重介紹第一點,即 ZNBase 團隊根據自身業務須要爲 Raft 模塊新增的三種角色。
ZNBase 對 Raft 模塊的改進
新增 Raft 角色
一、強同步角色
爲解決部署在跨地域的多數據中心數據同步問題,達到數據在多地共同寫入的效果,實現地域級別的容災能力,ZNBase 研發團隊在 etcd-raft 模塊中新增了強同步角色。
具體措施以下:
- 爲副本增長強同步標識以及配置和取消強同步標識的邏輯。
- etcd-raft 模塊原有的日誌提交策略:Leader 獲得超過半數副本(包括 Leader 自身)的投票才能提交 Raft 日誌。ZNBase 在原有的過半數提交策略基礎上,增長了強同步角色的日誌提交策略——日誌提交還須要獲得全部強同步副本的投票。
- 爲強同步角色設計瞭如圖 2 所示的故障識別與處理機制:經過心跳超時機制識別強同步故障,提交日誌時忽略故障的強同步角色,並將故障信息錄入數據庫日誌並告知用戶。
- 爲強同步角色的心跳超時時間增長熱更新功能。
圖2 強同步角色的處理邏輯
通過以上四點改造後,etcd-raft 模塊新加強同步角色,可以實現以下功能:
- 容許用戶爲指定副本配置或取消強同步角色,不影響強同步角色所在副本的原有特性。例如對全能型副本配置強同步角色後,該副本依然存儲 Raft 日誌和用戶數據,參與投票,參與 Leader 選舉,當選爲 Leader 可提供讀寫服務,Follower 時可提供非一致性讀。
- 強同步角色的數據與 leader 保持同步。
- 容許用戶配置強同步角色的心跳超時時間。若是強同步角色發生故障,Raft 集羣將在該超時時間後恢復寫入功能,故障期間的寫入也會在超時時間後提交。Raft會在識別到強同步角色故障後暫時取消強同步標記,並在該強同步角色故障解決後自動恢復。強同步角色故障和恢復的信息對用戶可見。
- 容許用戶在 SQL 終端查詢強同步角色的配置狀態。
二、只讀型角色
因爲 ZNBase 具有 HTAP 的特性,所以須要在 Raft Group 中增長一種相對獨立的特殊副本,對外僅提供讀服務(例如將該類型副本的存儲引擎替換成列存引擎)以實現 OLAP 的功能。爲了在 Raft Group 中增長這種特殊副本,同時不影響原有的集羣特性,ZNBase 研發團隊在 Raft 中設計了一種新的只讀型角色。
具體實現措施以下:
- 增長只讀型角色標識,增長只讀型角色的建立、刪除、重平衡邏輯。
- 增長只讀型副本接收到請求後讀取數據的邏輯:若是只讀型角色的時間戳不小於請求的時間戳,則提供讀服務;不然會重試屢次,重試達到限制後會將讀取超時錯誤返回到 SQL 終端。
- 爲只讀型副本的時間戳更新間隔、讀取時的最大重試次數等參數增長熱更新功能。
etcd-raft 模塊新增只讀型角色後,可以實現以下功能:
- 容許用戶在指定位置建立、刪除或移動只讀型角色。只讀型角色支持基於負載均衡的重平衡功能,移動到壓力相對較小的節點。
- 該角色對外僅提供讀服務,存儲 Raft 日誌和用戶數據,不參與投票,不參與 Leader 選舉,可提供讀服務。
- 容許用戶配置只讀型副本的時間戳更新間隔、讀取時的最大重試次數,能夠用於對只讀型副本的讀取性能進行調優。
- 容許用戶在 SQL 終端查詢只讀型角色的配置狀況。
三、日誌型角色
ZNBase 不支持在兩數據中心三副本部署模式下提供雙活模式,不管如何部署副本的位置,總有一個數據中心擁有過半數的副本。當擁有過半數副本的數據中心故障時,另外一個數據中心因爲所擁有的可用副本數不知足過半數,會致使 ZNBase 沒法對外正常提供服務。爲解決此類問題,提升 ZNBase 的容災能力,同時充分利用和整合資源,避免出現資源閒置形成的浪費現象,提高雙活數據中心的服務能力,項目團隊在 etcd-raft 模塊增長了日誌型角色。
具體實現措施以下:
- 日誌型副本參與 Leader 選舉,擁有投票權,而且能夠成爲 Leader。在普通副本缺乏最新日誌的故障場景下,爲了恢復集羣的可用性,須要日誌型副本當選爲 Leader,並向其餘副本追加日誌,使得該副本擁有最新的日誌。而後發起 Leader 轉移,擁有最新日誌的副本當選爲 Leader 和 Leaseholder,完成集羣恢復。
- 日誌型副本不能發送快照。因爲日誌型副本不含用戶數據,若發送快照將致使其餘副本丟失數據,所以禁止日誌型副本發送快照。
- 日誌型副本不能成爲 LeaseHolder。禁止從日誌型副本讀取數據,且在日誌型副本成爲Leader 的狀況下,在其餘副本擁有最新日誌後,將當即轉移 Leader 到該副本上。
- 日誌型副本保留日誌。日誌型副本的日誌可用於故障恢復,所以延長其日誌保留時間。原有的日誌清理策略爲當可清理的日誌 index 數量大於等於 100 或實際大小大於等於 64KB 時,執行日誌清理操做。當出現節點宕機時,待清理的日誌超過 4MB 執行日誌清理操做。日誌型副本的日誌清理策略爲:將日誌清理請求按小時打包、延遲處理,默認清理時間值爲24,即將日誌清理請求延遲24小時處理,達到日誌保留的效果。用戶可配置清理時間值,可配置範圍是[-1, MaxInt],若配置爲 -1 則表示不保留日誌,按照原來的邏輯執行清理操做。
- 日誌型副本異地重啓。日誌型副本異地重啓時會因嘗試提交心跳消息攜帶的 Commit 值而宕機。修改 follower 處理心跳的邏輯,若是日誌型 follower 收到心跳消息的 Commit 值比實際的 lastIndex 值大,就將心跳回復消息的 Reject 字段置爲 true、RejectHint 字段置爲實際的 lastIndex。Leader 收到 Reject 爲 true 的心跳回復消息時,將對應 follower 副本 progress 的 Match 和 Next 更新爲實際值,並向該副本追加日誌,將其丟失的日誌補全。
針對日誌型角色增長 Logonly 語法支持,使用 Alter 語句配置樣例以下,表 t 擁有 3 個副本,其中將 2 個全能型副本放在北京和濟南,日誌型副本放在天津:
ALTER TABLE t CONFIGURE ZONE USING num_replicas=2, num_logonlys=1, constraints='{"+region=beijing": 1,"+region=jinan": 1}', logonly_constraints='{"+region=tianjin":1}';
以兩中心三副本(一個全能型、一個強同步、一個日誌型)模式進行部署爲例,全能型副本與強同步副本分別存放在 DC-1 與 DC-2 的高配機器(或多數機器)中,日誌型副本存放在 DC-1 或 DC-2 的低配機器(或少許機器)中,日誌增量複製到另外一個數據中心的低配機器(或少許機器)。若遭遇數據中心級別的故障,在失去兩個副本(一個全能型、一個日誌型)後,在另外一個數據中心手動啓動存放日誌型副本的節點,該日誌型副本含有基於增量複製獲得的日誌數據。
遭遇數據中心級別的故障時的容災處理(Node7 的日誌型副本須要手動重啓)
經過給 Raft 算法增長 3 種新的角色,ZNBase 在跨地域集羣容災、支持 OLAP 等方面的能力獲得了顯著增強。
小結
本文介紹了 Raft 一致性算法在分佈式 NewSQL 數據庫 ZNBase 中發揮的重要做用,以及 ZNBase 項目團隊根據自身業務特性與需求,在 Raft 算法中新設計的三種角色,從而提升了 ZNBase 的異地容災和 HTAP 的能力。除了新增 Raft 角色之外,ZNBase 研發團隊還針對 Raft 模塊作了新增 Leader 親和選舉、混合序列化、Raft Log 分離與定製存儲和 Raft 心跳與數據分離等優化改進,受篇幅限制,咱們將在接下來的文章中詳細解析這四大改進:
關於 ZNBase 的更多詳情能夠查看:
官方代碼倉庫:https://gitee.com/ZNBase/zn-kvs
ZNBase 官網:http://www.znbase.com/
對相關技術或產品有任何問題歡迎提 issue 或在社區中留言討論。
同時歡迎更多對分佈式數據庫感興趣的開發者加入咱們的團隊!
聯繫郵箱:haojingyi@inspur.com
延伸閱讀