PolarDB-X 一致性共識協議 (X-Paxos)

背景

分佈式一致性算法(Consensus Algorithm )是一個分佈式計算領域的基礎性問題,其最基本的功能是爲了在多個進程之間對某個(某些) 值達成一致(強一致),進而解決分佈式系統的可用性(高可用)。Paxos是最重要的分佈式一致性算法,很多人都把它作爲“分佈式一致性協議”的代名詞(Mike Burrows, inventor of the Chubby service at Google, says that “there is only one consensus protocol, and that’s Paxos”)。

回顧Paxos的理論從1990年提出到現在已經有近30年了,但是真正工業級、獨立的Paxos基礎庫還是相當的少見。Google並沒有開源其任何Paxos基礎庫(連包含Paxos的項目都沒有開源過);Facebook也沒有公佈過包含paxos的產品; Apache有Zookeeper,但是其協議並不能支持一個高吞吐的狀態機複製,且並沒有提供獨立的第三方庫,可供快速接入;在Github上能找到的Paxos的獨立庫,star數最高是騰訊雲開源的phxpaxos庫,18年之後也基本沒有更新。

近幾年NewSQL和雲原生數據庫的不斷興起,極大地推動了關係數據庫和一致性協議的結合,PolarDB-X也是在這樣的背景下應運而生。

X-Paxos 誕生和發展

2014年,阿里隨着業務的高速增長,同城主備部署的方式已經無法滿足阿里對可擴展的部署、國際化、以及容災方面的需求,“異地多活”成爲了公司應用的新標準。基於這樣的業務背景驅動,PolarDB-X早期,在阿里集團MySQL設計了分佈式一致性協議模塊,並把它獨立命名爲X-Paxos,基於單機MySQL實現了一致性能力,配合TDDL分庫分表的模式部分解決了業務訴求。隨着技術的不斷髮展和演進,以及面向雲的時代的全面普及,我們PolarDB-X 2.0中融合了分佈式SQL引擎和基於X-Paxos的數據庫存儲技術,提供全新的雲原生分佈式數據庫。

在PolarDB-X 2.0中,我們也進一步擴展了分佈式和Paxos的協同,比如多副本的一致性讀、副本的動態遷移和管理能力等。反過來,PolarDB-X有了X-Paxos的加持,可以做到金融級數據庫的高可用和容災能力,做到RPO=0的生產級別可用性。Paxos協議對於面向雲的架構是非常必要的,雲的本質是虛擬化和資源池化,節點的變化和彈性是一個常規操作,我們需要解決面向用戶透明運維的能力,任何情況下數據都不能丟、不能錯。

除此以外,X-Paxos除了爲數據庫解決了分佈式一致性問題,同樣可以快速賦予其他系統分佈式一致性能力。我們把Paxos的能力獨立成一個基礎庫,希望能夠把這個能力帶給更多的其他系統。ps. 我們也做過快速的嘗試,把X-Paxos融入到單機KV數據庫RocksDB中,就可以很快速實現了一個分佈式KV引擎。

Google的論文《Paxos made live》中有一段話說的很好,大意是說:Paxos從理論到現實世界的實現之間有巨大的鴻溝,在真正實現一個Paxos的時候,往往需要對Paxos的經典理論做一些擴展,(尤其是在實現一個高性能的Paxos的時候,擴展點就更多了,可以參考後文的功能增強和性能優化),這往往會導致真正的Paxos實現其實都是基於一個未被完全證明的協議。這也就是傳說中,理論證明一個Paxos的實現,比實現這個Paxos還要難的原因了。因此一個成熟的Paxos實現很難獨立產生,往往需要和一個系統結合在一起,通過一個或者多個系統來驗證其的可靠性和完備性。這也是爲什麼大部分成熟的Paxos案例都是和分佈式數據庫相結合的,例如最早的Paxos實現(Chubby),當前的主要Paxos案例(Google的MegaStore、Spanner,AWS的DynamoDB、S3等)。而X-Paxos正是依託於PolarDB-X驗證了其可靠性和完備性。

X-Paxos 整體架構

X-Paxos的整體架構如上圖所示,主要可分爲網絡層、服務層、算法模塊、日誌模塊4個部分。

網絡層

網絡層基於libeasy網絡庫實現。libeasy的異步框架和線程池非常契合我們的整體異步化設計,同時我們對libeasy的重連等邏輯進行了修改,以適應分佈式協議的需求。

服務層

服務層是驅動整個Paxos運行的基礎,爲Paxos提供了事件驅動,定時回調等核心的運行功能。每一個paxos實現都有一個與之緊密相關的驅動層,驅動層的架構與性能和穩定性密切相關。

X-Paxos的服務層是一個基於C++11特性實現的多線程異步框架。常見的狀態機/回調模型以其開發效率低,可讀性差等缺點,一直被開發者所詬病;而協程又因其單線程的瓶頸,而使其應用場景受到限制。C++11以後的新版本提供了完美轉發(argument forwarding)、可變模板參數(variadic templates)等特性,爲我們能夠實現一種全新的異步調用模型提供了可能。

例如這是X-Paxos內實際的一行創建單次定時任務的代碼

new ThreadTimer(srv_->getThreadTimerService(), srv_, electionTimeout_, ThreadTimer::Oneshot,
                &Paxos::checkLeaderTransfer, this, targetId, currentTerm_.load(), log_->getLastLogIndex());

注:*左右滑動閱覽

以上一行程序,包含了定時器的創建,任意回調函數的設置,回調函數參數的轉發,並保證在回調觸發後(Oneshot)內存的自動回收。

算法模塊

X-Paxos當前的算法基於強leadership的multi-paxos[3]實現,大量理論和實踐已經證明了強leadership的multi-paxos,性能好於multi-paxos/basic paxos,當前成熟的基於paxos的系統,都採用了這種方式。

算法模塊的基礎功能部分本文不再重複,感興趣的同學可以參考相關論文[1,2,4]。在基礎算法的基礎上,結合阿里業務的場景以及高性能和生態的需求,X-Paxos做了很多的創新性的功能和性能的優化,使其相對於基礎的multi-paxos,功能變的更加豐富,性能也有明顯的提升。後面將對這些優化進行詳細的介紹。

日誌模塊

日誌模塊本是算法模塊的一部分,但是出於對極致性能要求的考慮,我們把日誌模塊獨立出來,並實現了一個默認的高性能的日誌模塊;有極致性能以及成本需求的用戶,可以結合已有的日誌系統,對接日誌模塊接口,以獲取更高的性能和更低的成本。這也是X-Paxos作爲高性能獨立庫特有的優勢,後面也會對這塊進行詳細介紹。

X-Paxos 特色功能

在線添加/刪除節點,在線轉讓leader

X-Paxos在標準multi-paxos的基礎上,支持在線添加/刪除多種角色的節點,支持在線快速將leadership節點轉移到其他節點(有主選舉)。這樣的在線運維能力,將會極大地方便分佈式節點的有計劃性的運維工作,將RTO降低到最低。

策略化多數派和權重化選主

阿里目前多地架構會有中心機房的訴求,比如:應用因其部署的特點,往往要求在未發生城市級容災的情況下,僅在中心寫入數據庫,數據庫的leader節點在正常情況下只在中心地域;同時又要求在發生城市級容災的時候(同一個城市的多個機房全部不可用),可以完全不丟失任何數據的情況下,將leader點切換到非中心。

而經典的multi-paxos並不能滿足這些需求。經典理論中,多數派強同步以後即可完成提交,而多數派是非特定的,並不能保證某個/某些節點一定能得到完整的數據,並激活服務。在實際實現中,往往地理位置較近的節點會擁有強一致的數據,而地理位置較遠的節點,一直處於非強一致節點,在容災的時候永遠無法激活爲主節點,形同虛設。

同時當中心單節點出現故障需要容災的時候,往往需要將主節點就近切換到同中心的另外一個節點,而經典理論中同樣沒有類似的功能。

X-Paxos在協議中實現了策略化多數派權重化選主

  1. 基於策略化多數派,用戶可以通過動態配置,指定某個/某些節點必須保有強一致的數據,在出現容災需求的時候,可以立即激活爲主節點。

  2. 基於權重化選主,用戶可以指定各個節點的選主權重,只有在高權重的節點全部不可用的時候,纔會激活低權重的節點。

節點角色定製化(Proposer/Accepter/Learner的獨立配置)

在經典的multi-paxos實現中,一般每個節點都包含了Proposer/Accepter/Learner三種功能,每一個節點都是全功能節點。但是某些情況下我們並不需要所有節點都擁有全部的功能,例如:

  1. 經典的三個副本部署中,我們可以裁剪其中一個節點的狀態機,只保留日誌(無數據的純日誌節點,但是在同步中作爲多數派計算),此時我們需要裁剪掉協議中的Proposer功能(被選舉權),保留Accepter和Learner功能。

  2. 我們希望可以有若干個節點可以作爲下游,訂閱/消費協議產生的日誌流,而不作爲集羣的成員(不作爲多數派計算,因爲這些節點不保存日誌流),此時我們裁剪掉協議的Proposer/Accepter功能,只保留Learner功能。

當然還有其他的組合方式,通過對節點角色的定製化組合,我們可以開發出很多的定製功能節點,即節約了成本,又豐富了功能。比如下面的三副本部署,其中一個Follower節點配置爲僅做logger模式(參與多數派投票,但不存儲數據),這樣可以將三副本的3份數據成本優化爲只有2份:

Witness SDK

基於上節節點角色定製化中的單獨Learner角色的功能,引發了無窮的想象力。Learner角色,可以抽象成一個數據流訂閱者(Witness Node),整個集羣中可以加入無數個訂閱者,當有新的日誌被提交的時候,這些訂閱者會收到其關心的日誌流,基於訂閱者功能,我們可以讓一個集羣很容易的實現下游訂閱消費,日誌即時備份,配置變更推送等等的功能。

因此我們把Learner角色單獨封裝成了一個SDK。基於這個SDK,用戶可以快速的爲自己的集羣添加,訂閱註冊,流式訂閱定功能;結合特定的用途打造一個完成的生態。採用了X-Paxos也可以利用Witness SDK快速實現分佈式系統和下游的其他系統的對接,形成一個完整的生態。

Leader 主動回切

在現實應用場景中,Follower 和 Leader 的狀態機難免會存在回放延遲,比如一個大的 DDL 會導致 Follower 的回放延遲被無限放大,而如果在回放延遲存在的情況下 Leader 掛掉新主選出時,新主無法對外提供服務,而此時老 Leader 可能已經重啓恢復,所以在這種情況下 X-Paxos 會主動探測狀態機的健康狀況,如果在一段時間內回放延遲無法追平,則會嘗試 Leader 主動回切,讓沒有回放延遲的老 Leader 對外提供服務。

多連接

傳統方式leader和learner之間採用單連接同步數據,在跨IDC場景中,網絡延時往往比較大,會導致leader和learner之間的數據差異較大。leader和learner之間通過多連接併發的發送數據,可以有效提升吞吐,減少數據差異,在弱一致的模式下,可以更好地對外提供讀能力。同時在單IDC出現完全不可用的情況下,提供更好的災備能力。

X-Paxos 性能優化

Batching & Pipelining

X-Paxos除了設計之初的強一致和高可用以外,其高性能也是至關重要的,尤其是應用於PolarDB-X分佈式數據庫,對協議的吞吐和延遲都提出了很高的要求。同時作爲可全球部署的分佈式一致性協議,在高延遲下的性能挑戰變得尤爲重要。

X-Paxos針對高延遲網絡做了大量的協議優化嘗試和測試,並結合學術界現有的理論成果[5,6,7]通過合理的Batching和Pipelining,設計並實現了一整套自適應的針對高延遲高吞吐和低延遲高吞吐網絡的通信模式,極大地提升了X-Paxos的性能。

  1. Batching是指,將多個日誌合併成單個消息進行發送;Batching可以有效的降低消息粒度帶來的額外損耗,提升吞吐。但是過大Batching容易造成單請求的延遲過大,導致併發請求數過高,繼而影響了吞吐和請求延遲。

  2. Pipelining是指在上一個消息返回結果以前,併發的發送下一個消息到對應節點的機制,通過提高併發發送消息數量(Pipelining數量),可以有效的降低併發單請求延遲,同時在transmission delay小於propagation delay的時候(高延遲高吞吐網絡),有效提升性能。

R爲網絡帶寬,D爲網絡傳播延遲(propagation delay,約爲RTT/2),經推導可知 Batching(消息大小:M)和Pipeling(消息併發:P)在如下關係下,達到最高吞吐。

M/R * P = D

X-Paxos結合以上理論,通過內置探測,針對不同節點的部署延遲,自適應的調整針對每個節點的Batching和Pipeling參數,達到整體的最大吞吐。因Pipeling的引入,需要解決日誌的亂序問題,特別是在異地場景下,window加大,加大了亂序的概率。X-Paxos實現了一個高效的亂序處理模塊,可以對底層日誌實現屏蔽亂序問題,實現高效的亂序日誌存儲。

多線程的全異步Paxos庫

由於Paxos的內部狀態複雜,實現高效的單實例多線程的Paxos變成一個非常大的挑戰。比如開源產品phxpaxos、Oracle MySQL Group Replication中使用的xcom,都是單線程的實現。phxpaxos採用了單分配單線程,多實例聚合的方式提升總吞吐,但是對單分區的性能非常的有限;xcom是一個基於協程的單線程實現。單線程的Paxos實現,在處理序列化/反序列化,分發、發包等邏輯的時候都爲串行執行,性能瓶頸明顯。

X-Paxos完全基於多線程實現,可以在單個分區Paxos中完全的使用多線程的能力,所有的任務都有通用的worker來運行,消除了CPU的瓶頸。依賴於服務層的多線程異步框架和異步網絡層,X-Paxos除了必要的協議串行點外,大部分操作都可以併發執行,並且部分協議串行點採用了無鎖設計,可以有效利用多線程能力,實現了Paxos的單分片多線程能力,單分區性能遠超競品,甚至超過了競品的多實例性能。

可插拔日誌

X-Paxos和現有的大部分Paxos庫很大的不同點就是X-Paxos支持可插拔的日誌模塊。日誌模塊是Paxos中一個重要的模塊,它的持久化關係到數據的一致性,它的讀寫性能很大程度上會影響協議整體的讀寫性能。當前大部分獨立Paxos庫都是內置日誌模塊,並且不支持插拔的。這會帶來2個弊端:

  1. 默認的日誌模塊提供通用的功能,很難結合具體的系統做針對性的優化

  2. 現有的系統往往已經存在了WAL(Write Ahead Log),而Paxos協議中需要再存一份。這使得 a) 單次commit本地需要sync 2次(影響性能);b) 雙份日誌浪費了大量的存儲

例如:phxpaxos內置的日誌模塊採用LevelDB,作爲日誌系統其操作大量冗餘,無針對優化;同時採用phxpaxos的phxsql單節點需要即保存binlog,又保存paxos log(在獨立的phxbinlogsvr中),會有性能性能、以及浪費存儲空間。而採用X-Paxos的MySQL存儲引擎可直接改造現有的binlog模塊,對接到X-Paxos的日誌模塊,單節點僅一份日誌,既降低了存儲,又提高了性能。

X-Paxos 正確性驗證

《Paxos made live》中有過一個說法,證明一個Paxos實現是正確的,比實現這個Paxos本身會更難。因此我們在設計和實現X-Paxos的時候,投入了大量的精力在Paxos的原理證明和實現驗證。

  1. Jepsen
  • Jepsen是開源社區比較公認的分佈式數據庫的測試框架。Jepsen驗證過包括VoltDB、CockroachDB、Galera、MongoDB、etcd在內的幾乎所有的主流分佈式數據庫/系統,檢驗出了不少的問題。

  • X-Paxos完成了和Jepsen的對接,並驗證了各個分佈式數據庫已有的case。

  1. TLA+
  • TLA+是Paxos創始人、圖靈獎獲得者Leslie Lamport老爺子發明的一種形式化規約語言。TLA+專用於設計、建模和驗證分佈式併發系統。Amazon DynamoDB/S3/EBS和Microsoft Cosmos DB都通過TLA+的模型驗證發現了不少問題

  • X-Paxos目前已經通過了TLA+的模型驗證。

  1. 隨機異常系統
  • 我們搭建了一套自動隨機異常驗證系統,可以自動化驗證各種異常場景下的協議正確性和穩定性。驗證X-Paxos在模擬網絡丟包、閃斷、隔離,磁盤故障等情況下的功能正確和數據一致。
  1. 異常回歸系統
  • X-Paxos擁有一套異常case迴歸系統,對於曾經出現過或者預期的併發異常case,都會加到異常case庫中,進行日常回歸驗證。同時異常case庫也在不斷的豐富中。

未來

Paxos是分佈式系統的基石,即使是近幾年,學術界關於Paxos的文章,新的演進方向一直在不斷的湧現,我們的PolarDB-X分佈式數據庫也會在X-Paxos的基礎上不停的發展,比如多分區Paxos、基於Paxos多副本的HTAP架構、以及Geo-Partition的特性等。


參考文檔

[1] The part-time parliament
[2] The Chubby lock service for loosely-coupled distributed systems
[3] Paxos Made Simple
[4] Paxos Made Live - An Engineering Perspective
[5] Everything You Ever Wanted to Know About Message Latency
[6] Adaptive Batching for Replicated Servers
[7] Tuning Paxos for high-throughput with batching and pipelining
[8] The Totem single-ring ordering and membership protocol
[9] Group Replication: A Journey to the Group Communication Core
[10] Mencius: Building Efficient Replicated State Machines for WANs

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章