CnosDB有主複製演進歷程

CnosDB發表於2023-12-06


分散式系統架構模式

分散式儲存系統下按照資料複製方式的不同,常分為兩種模式:主從模式、無主節點模式。

主從模式

主從模式以Raft分散式演算法為代表,Raft 演算法是 Diego Ongaro 和 John Ousterhout 於 2013 年發表的《In Search of an Understandable Consensus Algorithm》中提出;從工程案例etcd實現之後,Raft演算法開始大放異彩。

Raft分散式演算法流程

無主模式

無主模式以亞馬遜的Dynamo模型為代表,其理論來源於亞馬遜在2007年發表的《Dynamo: Amazon’s Highly Available Key-value Store》,在該論文中論述了一種無主節點的資料庫設計方案,像Cassandra、Riak等都是基於此理論實現。

Cassandra資料寫入流程

CnosDB整體架構

CnosDB 架構中主要包括了兩類程式: cnosdb、cnosdb-meta。cnosdb負責資料的寫入、查詢、以及儲存;cnosdb-meta負責後設資料儲存,像使用者、租戶、Database、Table、以及資料分佈等相關資訊。

Meta服務

CnosDB的Meta服務是有主節點的分散式系統,透過 Raft 一致性協議實現的供CnosDB使用的一套分散式配置中心。作為配置中心要求具有較高的資料一致性,在有主模式下較容易實現這一點,這是我們採用有主模式實現Meta服務的主要原因。

CnosDB 對訪問頻繁的Meta資料在本地有一份Cache,正常情況下直接訪問本地Cache,降低使用者的請求時延以及對Meta服務的訪問壓力;透過訂閱Meta服務的資料變更日誌來更新本地快取資料。

上面提到Meta資料的本地快取是透過訂閱變更的方式進行同步,這必然會帶來本地快取與Meta服務資料不一致性的問題。對於這個問題我們根據對資料的不同使用場景做了不同處理;在可以容忍這種不一致性的地方直接讀取本地快取,像絕大多數的正常的讀寫流程都不需要訪問Meta服務,這極大降低了使用者訪問時延;如果需要高度一致性的場景直接訪問Meta服務獲取最新的資料,像建立資料庫、表結構的變更等。

CnosDB服務

CnosDB服務負責資料的寫入、讀取和儲存,是一套類 Dynamo的無主節點儲存系統。

CnosDB按照兩個維度對資料進行分片儲存:時間分片與Hash分片。

時間分片:由於時序資料庫本身的特徵,按照時間維度進行分片有其天然的優勢。CnosDB每隔一段時間建立一個Bucket,每個Bucket都有起始、結束時間用於儲存對應時間段內的資料。每當系統容量不足時,新增新的儲存節點;在建立新的Bucket時也將優先使用空閒容量最多的新節點,已有資料也不需要移動,這完美地解決了叢集擴容與資料儲存容量的問題。

Hash分片 :時間分片完美地解決了儲存容量的問題;在一個分散式叢集環境下,會有多臺機器提供服務,如若每個Bucket只分布在一臺機器節點上,則整個系統的寫入效能受限於單臺機器。時序資料庫還有另外一個概念是SeriesKey,按SeriesKey進行Hash分片可以分割成不同的 ReplicaSet (複製組),每個 ReplicaSet 內的一個副本稱其為Vnode,每個Vnode分散在不同的儲存節點上,一個 ReplicaSet 內的多個Vnode按照 Dynamo方式進行副本間資料同步 ;Vnode是CnosDB進行資料分片管理的基本單元,採用Hash分片的方式解決了系統吞吐量的問題。

如下圖所示,在時間維度上每隔一段時間建立一個Bucket,每個Bucket有兩個複製組(用不同顏色來區分),每個複製組有兩個副本(一個Bucket內相同顏色的Vnode有兩個)分別落在不同的儲存節點上。

當前面臨問題

按照時間、Hash兩個維度進行分片解決分散式儲存下容量與效能問題;但由於每個複製組內的資料複製方式是類 Dynamo的無主節點模式,這帶來一些新問題:

資料一致性 :在無主節點的分散式儲存系統中解決資料不一致性問題方式也有很多,像Cassandra中採用以最後寫入者為準,Riak中的向量時鐘等;再配合讀修復、反熵、提示移交(Hinted Handoff)等一整套機制達到資料的最終一致性。這一整套機制實現起來是較為複雜的,也會極大拖累系統的整體效能,CnosDB當前也只是實現了部分機制。

資料訂閱與同步 :在無主節點的分散式儲存系統中由於資料寫入無順序性,這導致做資料訂閱也是一個難以解決的問題。在無主節點的模式中資料寫入的統一的入口就是計算節點的介面層,但是在計算儲存分離的強大背景下,通常計算層都是無狀態的,在這一層做資料訂閱難以保證訂閱服務不丟失資料。

當Raft遇上CnosDB

為解決上述問題,我們轉投有主模式尋求解決方案。

半同步方案 :最開始我們參考Kafka設計了一個半同步方案。在Kafka中透過下面幾個引數來控制服務在不同模式下執行,給使用者靈活自由的選擇。(不熟悉的讀者,具體引數含義可以查閱網路資訊)

  1. acks
  2. 複製因子(副本數)
  3. min.insync.replicas

此方案最大優點就是靈活,使用者需要什麼樣的一致性、可靠性等級系統都可以透過引數進行配置;我們考慮到實現的難度、時間關係以及之後的穩定性,暫停了此方案。

其他方案 :我們還討論了主備節點的方案;由於CnosDB系統是多租戶的,每個租戶甚至每個Database要求的副本數都不一致,導致有些副本節點比較空閒、而像主節點則會壓力比較大,造成機器間負載不均衡,資源的浪費等。

最終,我們選擇了成熟的Raft方案,確切說是Multi-raft的方式,CnosDB中每個ReplicaSet都是一個Raft複製組。

為什麼選擇Raft

自採用Raft共識演算法實現的etcd釋出以後,Raft演算法被公認為易於理解、相對易於實現的一套分散式共識演算法,在資料的一致性、穩定性、可靠性等方面都得到眾多產品的驗證。

Raft是有主模式,會很好滿足對資料一致性的要求;同時做資料訂閱也極其容易(一種方式是作為叢集的Learner節點存在,再一種方式是直接讀取 Raft Logs)。

再就是CnosDB的Meta服務已經採用Raft。CnosDB主程式再也沒有理由不去擁抱Raft。

關於Raft開源社群也有眾多開源實現,幾乎成為當前分散式系統下的標準演算法,我們也並沒有必要從頭開始造輪子,而是選擇了openraft方案。

Raft在CnosDB中應用

在CnosDB 中一個ReplicaSet就是一個Raft複製組,一個ReplicaSet由分佈在不同機器上的多個Vnode副本組成,這些Vnode便構成了該Raft複製組的成員節點。

Raft在CnosDB中使用

資料寫入過程 :在CnosDB中每次寫入都對應Raft複製組中的一次提交。服務接收到請求後判斷本節點是否是此次寫入對應Raft複製組的主節點,不是則進行請求轉發;如果是則透過Raft協議進行寫入,按照Raft協議在每個Vnode節點下產生一條Raft Log Entry,CnosDB會以WAL的形式記錄到此Vnode目錄下。

有超過半數Vnode節點記錄WAL成功後,由Raft協議驅動進入下一個階段——Apply階段;在CnosDB中Apply階段較為簡單,只是寫Memcache,主節點Apply成功後就響應客戶端成功。

服務重啟後Memcache中的資料透過WAL進行恢復。Memcache是由上層的Raft協議驅動刷盤,每次Memcache的刷盤都是作為一個Snapshot而存在。

更詳細的過程可以參考Raft協議相關文件。

資料讀取過程 :根據查詢條件,資料讀取最終會落到每個Vnode上。在CnosDB的計劃中是優先讀取主節點的Vnode,這會帶來較高的資料一致性;由於CnosDB是一個Multi-raft實現方式,主節點是分散在各個節點並不會造成主從節點負載不均勻的問題。

新的問題

Raft共識演算法在如下場景下並沒有說明如何處理:

  1. 讀寫Raft log發生錯誤
  2. Apply時發生錯誤

上述兩項錯誤通常為IO錯誤,發生上述錯誤時系統將陷入一種未知的狀態,當前CnosDB的做法是儘量提前預檢查規避錯誤的發生,也參考了etcd的處理方式如果遇到不可處理的未知場景,將由人工介入處理。

啟用Raft演算法

在配置檔案中只需要把配置項using_raft_replication設定為true即可。

using_raft_replication = true

總結

CnosDB在2.4版本中引入Raft共識演算法,在保住了CnosDB高效能、高可用性、易維護性的提前下,將會使得CnosDB 查詢具有更高的資料一致性,同時為後面待實現的資料訂閱、同步、資料異構處理做足了前期準備。

隨著CnosDB各項功能的完善,未來將會在更廣泛的使用場景中發揮更大的作用。後期我們會推出更多的測試報告、以及使用場景供大家參考,歡迎各位關注我們,我們共同成長。


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/70026635/viewspace-2999016/,如需轉載,請註明出處,否則將追究法律責任。

相關文章