PolarDB-X 存儲引擎核心技術 | Paxos 多副本

PolarDB-X作爲PolarDB分佈式版,是阿里巴巴自主設計研發的高性能雲原生分佈式數據庫產品,爲用戶提供高吞吐、大存儲、低延時、易擴展和超高可用的雲時代數據庫服務。PolarDB-X在架構上可以簡單分爲CN節點和DN節點。計算節點CN負責SQL的解析和執行,存儲節點DN負責數據的分佈式事務和高可用存儲。本文主要對存儲引擎核心中高可用部分詳細技術解讀。

背景

傳統的存儲引擎高可用主要採用主備同步的方式,但是主備同步方式天然存在以下限制:

  • 高性能與數據一致性的取捨:主備複製採用強同步方式會導致主庫寫入性能降低,採用異步方式無法保證主庫宕機時備庫無損提供一致性保證,半同步方式雖然能有效緩解性能降低,但同樣無法提供嚴格的一致性保證
  • 系統可用性:主備庫方式在面臨網絡故障或延遲較大場景時,可能會存在主庫遲遲無法提交事務,不能及時主備切換,從而影響系統的可用性

針對主備庫方式的問題,當前業界主要採用基於多副本的高可用存儲引擎方案。PolarDB-X同樣基於該路線,自研了分佈式一致性協議模塊 X-Paxos,並將其與MySQL引擎深度結合,形成了PolarDB-X的多副本高可用存儲引擎。

特性

下圖是PolarDB-X多副本存儲節點的基本架構,展示了一個部署三個副本的PolarDB-X DN集羣。PolarDB-X DN集羣是一個單點寫入,多點可讀的集羣系統。在同一時刻,整個集羣中至多會有一個Leader節點來承擔數據寫入的任務。PolarDB-X DN節點的每個實例都是一個單進程的系統,X-Paxos被深度整合到了數據庫內核之中,替換了原有的複製模塊。集羣節點之間的增量數據同步通過X-Paxos來驅動完成,不再需要外部手動指定複製位點。

總的來說,PolaDB-X多副本存儲引擎具備以下特性:

  • 強一致性保障:X-Paxos基於強Leadership的Multi-Paxos實現,大量理論和實踐已經證明了強Leadership的Multi-Paxos,性能好於Multi-Paxos/Basic-Paxos。PolarDB-X在崩潰恢復時的一致性問題上更是做了深入的保障
  • 高性能的數據同步:具備Batching/Pipelining方式的高效日誌傳輸,具備多線程異步的高效數據傳輸,具備X-Paxos日誌和Binlog融合的極致精簡統一
  • 靈活的運維容災:在線添加/刪除/權重化配置任意節點,具備手動/自動選主的靈活策略
  • 低成本的數據存儲:創新引入Logger角色副本,讓三副本具備最低兩份存儲的數據存儲開銷

下面針對以上特性分別進行深入解讀。

原理

強一致性保障

強一致性保障主要依賴X-Paxos模塊,上圖展示的X-Paxos整體架構,整體可分爲網絡層、服務層、算法模塊、日誌模塊4個部分:

  • 網絡層:基於libeasy網絡庫實現。libeasy的異步框架和線程池非常契合我們的整體異步化設計,同時我們對libeasy的重連、日誌等邏輯進行了修改,以適應分佈式協議的需求
  • 服務層:驅動整個Paxos運行的基礎,爲Paxos提供了事件驅動,定時回調等核心的運行功能。每一個paxos實現都有一個與之緊密相關的驅動層,驅動層的架構與性能和穩定性密切相關。
  • 算法模塊:一致性協議的核心,包括基於強Leadership的Multi-Paxos實現的選主模塊,採用預讀緩衝區/熱緩衝區的數據管理模塊,複用Enhanced Multi-Threaded Slave技術的同步傳輸應用模塊,以及擴展sync_relay_log_info系統表的集羣元數據模塊。
  • 日誌模塊,原本是算法模塊的一部分,出於對極致性能要求的考慮,我們把日誌模塊獨立出來,並實現了一個默認的高性能的日誌模塊。

如果說算法模塊是運行時強一致性的保證,那麼日誌模塊就是容災時的強一致性保證。 日誌模塊和Binlog極致耦合,同時也是維護崩潰恢復邏輯的關鍵部分。Paxos多副本強一致算法網上介紹很多,這裏不再闡述。我們重點說明下PolarDB-X中的日誌模塊實現。

日誌模塊

在原有的MySQL主備複製模式中,Master節點負責寫入binary log,並提交事務。Slave節點通過IO線程從Master節點發起dump協議拉取binary log,並存儲到本地的relay log中。最後由Slave節點的SQL線程負責回放relay log。 默認情況Slave節點除了產生relay log,還會根據log-slave-updates有一份冗餘的binary log。

PolarDB-X存儲節點整合了binary log和relay log,實現了統一的consensus log,節省了日誌存儲的成本。當某個節點是Leader的時候,consensus log扮演了binary log的角色;同理當某個節點被切換成Follower/Learner時,consensus log扮演了relay log的角色。新的consensus log基於一致性協議和State Machine Replication理論,保證了多個節點之間的數據一致性。

日誌模塊也接管consensus log的同步邏輯,取消Relay IO線程,複用Relay SQL線程和Relay Worker線程,支持MTS多線程並行回放。同時提供對外的接口來實現日誌寫入和狀態機回放。


根據算法模塊和Binlog的依賴,日誌模塊引入了四種日誌類型:

  • Consensus Log Event:標記在每個事務的Anonymous_GTID日誌之前,用於記錄當前事務對應協議層index、term、flag等元信息
  • Previous Consensus Index Event:寫在每個binary log文件的最開始,Format_description events之後,Previous_gtids events之前,用於標記當前文件起始的事務index
  • Consensus Cluster Info Log Event:記錄多副本集羣配置變更的信息
  • Consensus Empty Log Event:記錄X-Paxos協議選主日誌

PolarDB-X日誌模塊同樣改造了MySQL原有事務提交的流程。MySQL的Group Commit分爲三個階段:flush stage、sync stage、commit stage。對於多副本的Leader節點,PolarDB-X DN節點在 Binlog 的 Flush 和 Sync 過程中將攜帶Consensus Log Event的Binlog 內容同時廣播到所有 Follower。所有進入commit stage的事務會被統一推送到一個異步隊列中,進入quorum決議的判定階段,等待事務日誌同步到多數節點上,滿足quorum條件的事務才允許commit,以保證數據的強一致。

此外,爲了保證高性能,Leader上consensus log的本地寫入和日誌同步可以並行執行對於Follower節點,SQL線程讀取consensus log,等待Leader的通知。Leader會定期同步給Follower每一條日誌的提交狀態,達成多數派的日誌會被分發給worker線程並行執行。

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

恢復邏輯

PolarDB-X的多副本存儲引擎的恢復流程和原生MySQL大不相同,不同在Binlog參與的恢復流程,需要X-Paxos協議層參與,並且需要重點解決原生MySQL XA 的不完整性問題。

PolarDB-X的多副本存儲引擎的恢復的流程如下:

1. 掃描Binlog文件

    • 掃描consensus日誌的完整性, 不完整的日誌需要整體切除。
    • 收集記錄完整提交Binlog的事務,構建提交事務的hash表
      • 確認最後一次實例存活時產生的Binlog文件。以用作恢復懸掛事務的依據
      • 必須保證所有懸掛的事務都是在最後一個Binlog中,即使發生過切主
      • relay log 也必須保證沒有懸掛事務, 因爲這裏會涉及到前一個作爲主的任期的懸掛事務

2. 掃描InnoDB,獲得所有的懸掛事務, 如果不在提交事務hash表,那麼回滾

3. 等待X-Paxos啓動,推進回放位點,根據回放位點確定懸掛事務的是否提交

a. 如果回放之前節點是處於Binlog WORKING 狀態,並且協議層也是leader role(leader 宕機恢復),則意味着協議層沒有降級,那麼可以判斷爲是在寫入的狀態下宕機恢復, 此時需要從系統表的last_leader_term 字段中根據最後是leader的term來遍歷日誌, 從此term最後一條日誌後開始作爲回放起點。
b. 如果回放前節點是處於Binlog WORKING 狀態,協議層已經不是leader role(協議leader降級,或者是降級後server狀態未變更時宕機),則意味着協議層已經降級,在降級的時候把commit index 寫入了start_apply_index。 此時就從start_apply_index後面的一條日誌開始回放。
c. 如果回放前節點是處於RELAY LOG WORKING狀態,協議層也是非leader(每次重啓非leader的回放線程),則這意味着此實例一直是一個非leader, 那麼找到slave_relay_log_info 表中記錄的consensus_apply_index 作爲回放位點
d. 如果回放前節點是處於RELAY LOG WORKING狀態,但是協議層已經切換到了leader (協議follower升級,或者是升級過程中server狀態未變更時宕機),則這意味着此實例一直是一個非leader, 那麼找到slave_relay_log_info 表中記錄的consensus_apply_index 作爲回放位點

4. 啓動apply 線程,完成恢復流程

5. 打開MySQL監聽端口

MySQL XA 實現了一套兩階段提交協議,以便在分佈式事務系統中使用,但原生的 MySQL XA 存在完整性問題,假如在分佈式場景中,Node1, Node2 ... NodeN 作爲節點參與方,XA 使用 2PC 保證分佈式事務的原子性,但對於每個節點中的 Binlog Storage 和 InnoDB Storage 兩個參與方,MySQL 目前沒有機制保證在每個階段(Prepare 或者 Commit) 不同 Storage 參與方之間的一致性,這也源於Binlog Storage 本身沒有 UNDO 的機制保證有關,這樣 2PC 的崩潰恢復協議也就無法獲得有效的節點事務狀態。

爲了保證嚴格的強一致性保證,PolarDB-X多副本存儲引擎除了使用兩階段提交協議的 External Coordinator 以外,引入了使用 GTID 補償協議的 Internal Coordinator 來保證 Storage 之間的一致性。

GTID 補償協議的主要工作原理:

  • 在單個節點崩潰恢復階段,根據 Binlog File 構建 Binlog GTID 集合;
  • 在單個節點崩潰恢復階段,根據 InnoDB GTID_EXECUTED 以及 TRANSACTION UNDO HISTORY 構建 InnoDB GTID 集合
  • 構建集合的差集,使用 Binlog Event 進行補償 InnoDB 丟失的事務,恢復單個節點
  • 完成單個節點的補償後,XA 組件開始分佈式集羣的兩階段提交事務的崩潰恢復

高性能數據同步

高吞吐的日誌傳輸

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

Batching是指將多個日誌合併成單個消息進行發送;Batching可以有效地降低消息粒度帶來的額外損耗,提升吞吐。但是過大Batching容易造成單請求的延遲過大,導致併發請求數過高,繼而影響了吞吐和請求延遲。
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變成一個非常大的挑戰。比如開源產品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個弊端:

  • 默認的日誌模塊提供通用的功能,很難結合具體的系統做針對性的優化
  • 現有的系統往往已經存在了WAL(Write Ahead Log),而Paxos協議中需要再存一份。這使得
  • 單次commit本地需要sync 2次,影響性能
  • 雙份日誌浪費了大量的存儲,增加了成本

PolarDB-X存儲節點整合了binary log和relay log,實現了統一的consensus log,既節省了日誌存儲的成本,又提高了性能。日誌融合的實際改動,前面強一致性保障中已經詳細描述,這裏不再展開。

靈活的運維容災

在線添加節點/刪除節點/切主

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

語法 含義
call dbms_consensus.change_leader("127.0.0.1:15700") 將 Leader 切換到指定節點
call dbms_consensus.upgrade_learner("127.0.0.1:15700") 將指定 Learner 升級成 Follower
call dbms_consensus.downgrade_follower("127.0.0.1:15700") 將指定 Follower 降級成 Learner
call dbms_consensus.add_follower("127.0.0.1:15700") 加 Follower,內部會先加爲 Learner,追平日誌之後自動轉成 Follower;
call dbms_consensus.add_learner("127.0.0.1:15700") 將指定節點以 Learner 身份添加到集羣
call dbms_consensus.drop_learner("127.0.0.1:15700") 將指定節點以 Learner 身份剔除出集羣

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

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

而經典的Multi-Paxos並不能滿足這些需求。經典理論中,多數派強同步以後即可完成提交,而多數派是非特定的,並不能保證某個/某些節點一定能得到完整的數據,並激活服務。在實際實現中,往往地理位置較近的節點會擁有強一致的數據,而地理位置較遠的節點,一直處於非強一致節點,在容災的時候永遠無法激活爲主節點,形同虛設。 同時當中心單節點出現故障需要容災的時候,往往需要將主節點就近切換到同中心的另外一個節點,而經典理論中同樣沒有類似的功能。

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

  • 基於策略化多數派,用戶可以通過動態配置,指定某個/某些節點必須保有強一致的數據,在出現容災需求的時候,可以立即激活爲主節點
  • 基於權重化選主,用戶可以指定各個節點的選主權重,只有在高權重的節點全部不可用的時候,纔會激活低權重的節點

低成本的數據存儲

在經典的Multi-Paxos實現中,一般每個節點都包含了Proposer/Accepter/Learner三種功能,每一個節點都是全功能節點。但是某些情況下我們並不需要所有節點都擁有全部的功能,例如: 1. 經典的三個副本部署中,我們可以裁剪其中一個節點的狀態機,只保留日誌(無數據的純日誌節點,但是在同步中作爲多數派計算),此時我們需要裁剪掉協議中的Proposer功能(被選舉權),保留Accepter和Learner功能。 2. 我們希望可以有若干個節點可以作爲下游,訂閱/消費協議產生的日誌流,而不作爲集羣的成員(不作爲多數派計算,因爲這些節點不保存日誌流),此時我們裁剪掉協議的Proposer/Accepter功能,只保留Learner功能。

多副本的技術雖然比主備同步的方式更加高性能和高可用,但多副本本身引入的多份數據存儲開銷難以避免的痛點。PolarDB-X多副本存儲引擎深入研究理解協議發現,在經典的Multi-Paxos實現中,一般每個節點都包含了Proposer/Accepter/Learner三種功能,每一個節點都是全功能節點。但是某些情況下我們並不需要所有節點都擁有全部的功能,例如:

  • 經典的三個副本部署中,我們可以裁剪其中一個節點的狀態機,只保留日誌(無數據的純日誌節點,但是在同步中作爲多數派計算),此時我們需要裁剪掉協議中的Proposer功能(被選舉權),保留Accepter和Learner功能。
  • 我們希望可以有若干個節點可以作爲下游,訂閱/消費協議產生的日誌流,而不作爲集羣的成員(不作爲多數派計算,因爲這些節點不保存日誌流),此時我們裁剪掉協議的Proposer/Accepter功能,只保留Learner功能。

PolarDB-X創新地引入Learner/Logger角色副本,通過對節點角色的定製化組合,我們可以開發出很多的定製功能節點,既節約了成本,又豐富了功能。

典型的三節點複製模式如下圖所示。通過對節點角色的定製化組合,在三副本部署,一個節點繼續作爲全功能副本Leader,一個節點繼續作爲Follower,另外一個Follower節點配置爲僅做Logger角色,這樣可以將三副本的三份數據成本優化爲只有兩份,同時不影響高可用的保障。再借助PolarDB-X完整兼容的MySQL Binlog,可以無縫兼容OSS、DTS、Canal等業內常用的Binlog增量訂閱工具,實現數據的異地低成本備份。

作者:冷香、嚴華

點擊立即免費試用雲產品 開啓雲上實踐之旅!

原文鏈接

本文爲阿里雲原創內容,未經允許不得轉載。

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