引言
水平擴展(Scale Out)對於數據庫系統是一個重要的能力。採用支持 Scale Out 架構的存儲系統在擴展之後,從用戶的視角看起來它仍然是一個單一的系統,對應用完全透明,因此,它可以使數據庫系統能有效應對不同的負載場景,對用戶非常用價值。
但是,數據庫本身是一個有狀態的系統,所以,它的水平擴展是一件比較困難的事情。數據庫通常需要管理著龐大的數據,系統在擴展期間,如何保證數據一致性、高可用以及系統整體的負載均衡,更是整個水平擴展的難點。
水平擴展按不同資源類型分類,可以細分為計算節點的水平擴展與數據節點的水平擴展,後文若無特別說明,水平擴展特指數據節點的水平擴展。而數據節點的水平擴展,按查詢請求的類型,也可以進一步劃分為讀能力的擴展與寫能力的擴展。
單機數據庫的擴展
MySQL 主從複製
在單機數據庫時代,數據庫的讀寫流量全集中在一臺物理機。所以,單機數據庫要做擴展, 一個簡單有效的思路,就是將單機數據庫的數據裡複製一份或多份只讀副本,然後應用自己做讀寫分離。
早期 MySQL(5.5及以下版本)基於主從複製協議實現了主備庫架構[17],依靠備庫實現了讀能力的擴展,但主庫與備庫之間同步是採用異步複製或半同步複製,備庫的數據總會有一定數據延遲(毫秒級或亞秒級)。
MGR 與 多主模式
後來 MySQL 在5.7引入了基於Paxos協議[14]的狀態機複製技術:組複製[13]功能,徹底解決了基於傳統主備複製中數據一致性問題無法保證的情況。組複製使MySQL可以在兩種模式下工作:
- 單主模式(Single-Master)。單主模式下,組複製具有自動選主功能,每次只有一個 Server成員接受寫入操作,其它成員只提供只讀服務,實現一主多備。
- 多主模式(Multi-Master)。多主模式下,所有的 Server 成員都可以同時接受寫入操作,沒有主從之分,數據完全一致,成員角色是完全對等的。
但是,無論是單主模式還是多主模式,都只能支持讀能力的擴展,無法支持寫能力的擴展(事實上,MGR的多主模式更多的作用是用於高可用與容災)。原因很好理解,即使在多主模式下,每個Server節點內實際所接收的寫流量(來自客戶端寫流量+來自Paxos協議的複製流量)是大致相同的。隨著Paxos Group的成員增多,寫放大的現象將越來越嚴重,將大大影響寫吞吐。此外,MGR採用樂觀衝突檢測機制來解決不同節點的事務衝突,因此在衝突頻繁的場景下可能會出現大量事務回滾,對穩定性影響很大。
分佈式數據庫的擴展
與單機數據庫不同,分佈式數據庫本身就是為了解決數據庫的擴展問題而存在。比如, 目前相對主流的以Google Spanner[10]、CockroachDB[5]、TiDB[4]等為代表NewSQL[9]數據庫,大多都是基於Shared-Nothing架構,並對數據進行了水平分區,以解決讀寫擴展問題;每個分區又通過引入Raft[14]/Paxos[15]等的一致性協議來實現多副本的強一致複製,並以此來解決分區的高可用與故障容災問題。因此,水平擴展在分佈式數據庫中是一個重要而基礎能力。
這裡將以 CockroachDB 為例來探討分佈式數據庫的水平擴展過程。CockroachDB 是一個參考Google Spanner[10]論文的實現的開源的 NewSQL 數據庫。一個 CockroachDB 集群是由多個 Cockroach Node 組成,每個Cockroach Node 會同時負責SQL執行與數據存儲。CockroachDB 底層的存儲引擎 RocksDB 會將數據組織成有序的 Key-Value 對形成一個KV map。
數據分區
為了支持水平擴展,CockroachDB 會按 KV map 的key的取值範圍,在邏輯上水平切分為多個分片,稱為 Range。每個 Range 之下會有多個副本(副本數目可配),這些副本會分佈在不同的 Cockroach Node 中, 並共同組成了一個 Raft Group,藉助 Raft 協議進行強一致同步,以解決分區級別的高可用與故障容錯。Range中被選為 Raft Leader 的 Range 副本 稱為 LeaseHolder,它不但要負責承擔來自用戶端的寫入流量,還要負責 Range 後續的變更與維護(例如,Range 的分裂、合併與遷移);而其它非Leader 的副本則可以承擔讀流量。為了管理整個系統的元數據,CockroachDB 中有一個特殊 Range,名為 System Range,用以保存各個 Table 元數據及其它各個 Range 的物理位置信息。然後,CockroachDB 會基於實際負載情況與資源情況,去調度各個Range 的 LeaseHolder,讓它們均衡地散落在各個 Cockroach Node,以達到讀寫流量能均攤到不同機器節點的效果。
水平擴展
CockroachDB 在水平擴展新增加節點時,為了能將一些Range的流量調度到新加入的節點,它會反覆多次做兩個關鍵的操作:遷移 Range與分裂 Range。
遷移 Range。它可用於解決不同 Cockroach Node 之間負載不均衡。例如,當系統增加了 Cockroach Node,CockroachDB 的均衡調度算法會檢測到新增 Cockroach Node 與其它老 Cockroach Node 形成負載不均衡的現象,於是會自動尋找合適的 Range 集合,並通知這批 Range 的 LeaseHolder 自動切換到新增的 Cockroach Node 中。藉助 Raft 協議,LeaseHolder 要將自己從 Cockroach Node A 移動到 Cockroach Node B,可以按下述的步驟輕易完成:
- 先往 Raft Group 中增加一個新副本B(它位於 Cockroach Node B );
- 新副本B 通過回放全量的 Raft Log 來和 Leader 數據一致;
- 新副本B 完成同步後,則更新 Range 元數據,並且刪除源副本A(它位於 Cockroach Node A)。
分裂 Range。若 Range 數目過少時,數據無法被完全打散的,流量就會被集中少數 Cockroach Node,造成負載不均。因此,CockroachDB 默認了單個 Range 最大允許是64MB(可配置),若空間超過閾值,LeaseHolder 會自動對 Range 進行分裂。前邊說過,Cockroach Node 的 Range 劃分是邏輯劃分, 因此,分裂過程不涉及數據遷移。分裂時,LeaseHolder 會計算一個適當的候選Key作為分裂點,並通過 Raft 協議發起拆分,最後更新Range 元數據即可。
當 Range 經過多次分裂後,產生更多的LeaseHolder,CockroaachDB 的均衡調度算法就可以繼續使用遷移 Range 的操作讓讀寫流量分散到其它 Cockroach Node ,從而達到水平擴展且負載均衡的效果。
PolarDB-X 的水平擴展
PolarDB-X 作為阿里巴巴自主研發的分佈式數據庫,水平擴展的能力自然是其作為雲原生數據庫的基本要求。但談及分佈式數據庫的擴展能力,通常離不開其架構與分區管理兩個方面。接下來,我們通過介紹其架構與分區管理,再來詳細說明 PolarDB-X 的水平擴展實現方案。
架構
為了最大限度地發揮其雲數據庫的彈性擴展能力,PolarDB-X 一開始就決定採用了基於存儲計算分離的Shared-Nothing架構,以下是 PolarDB-X 的架構圖:
如上圖所示,PolarDB-X整個架構核心可分為3個部分:
- GMS(Global Meta Service):負責管理分佈式下庫、表、列、分區等的元數據,以及提供TSO服務;
- CN(Compute Node):負責提供分佈式的SQL引擎與強一致事務引擎;
- DN(Data Node):提供數據存儲服務,負責存儲數據與副本數據的強一致複製。
PolarDB-X 存儲層使用的是 X-DB 。因此,每一個 DN 節點就是一個 X-DB 實例(X-DB的介紹請看這裡)。X-DB 是在 MySQL 的基礎之上基於 X-Paxos 打造的具備跨可用區跨地域容災的高可用數據庫,使用 InnoDB 存儲引擎並完全兼容 MySQL 語法, 它能提供 Schema 級別的多點寫 與 Paxos Group 的 Leader 調度的能力 。PolarDB-X 的分區副本的高可用與故障容災就是建立在X-DB基礎上的。
相比其他同樣採用 Multi-Group Paxos / Raft 設計的 NewSQL,如 CockroachDB[5]、YugabyteDB[16]等,PolarDB-X 始終堅持與基於 MySQL 原有架構進行一體化的設計的 X-DB 相結合,一方面是考慮到這樣能讓 PolarDB-X 在MySQL相關的功能、語法以及上下游生態的兼容性與穩定性上更有優勢;另一方面是 X-DB 本身具有較強的複雜SQL處理能力(比如Join、OrderBy等),這使得 PolarDB-X 相對方便地通過向 X-DB 下推SQL來實現不同場景的計算下推(比如,Partition-wise Join 等),能大大減少網絡開銷,提升執行性能,是 PolarDB-X 的一個重要特性。
數據分區與表組
數據分區
與 CockroachDB 等分佈式數據庫類似, PolarDB-X 也會對數據進行切分。在 PolarDB-X 中,每個表(Table)的數據會按指定的分區策略被水平切分為多個數據分片,並稱之為分區(Partition)。這些分區會分佈在系統的各個DN節點中,而各個分區在DN的物理位置信息則由 GMS 來統一管理。每個分區在 DN節點中會被綁定到一個 Paxos Group,並基於 Paxos 協議構建數據強一致的多副本來保證分區組的高可用與容災,以及利用多副本提供備庫強一致讀。Paxos Group 會通過選舉產生分區 Leader, Leader 的分區組負責接收來自CN節點的讀寫流量;而 Follower 的分區組則負責接收CN節點的只讀流量。
分區策略
我們知道,分佈式數據庫常見的分區策略有多種,諸如 Hash / Range / List 等,分佈式數據庫往往選取其中的一種作為其內部默認的分區方式,以組織與管理數據。像前邊提及的 CockroachDB[5] 的默認分區策略是按主鍵做 Range 分區,YugabyteDB[16] 的默認分區策略則是按主鍵做一致性 Hash 分區。那麼,PolarDB-X 的默認分區策略採用是什麼呢?PolarDB-X 的默認分區也是採用一致性 Hash 分區,之所以這樣選擇,主要是基於兩點的考量:
- 對一個主鍵做範圍查詢的場景在實際情況中並不是很常見,與Range分區相比,一致性 Hash 分區能更有效將事務寫入打散到各個分區,能更好負載均衡;
- 分佈式數據庫在進行水平擴展,往往需要添加新的DN節點,採用一致性 Hash分區能做到按需移動數據,而不需要對全部分區數據進行rehash。
因此,相比於CockroachDB ,PolarDB-X 的默認的一致性Hash分區能更好均攤寫入流量到各個分區,這對後邊的動態增加DN節點做水平擴展很有意義。
除了默認分區策略,PolarDB-X也支持用戶自己指定分區 Hash / Range / List 等分區策略來管理各分區的數據, 關於 PolarDB-X 分區方式的更多的思考,讀者可以參考《探索 | PolarDB-X:實現高效靈活的分區管理》這篇文章。
表組與分區組
在PolarDB-X中,為加速SQL的執行效率,優化器會嘗試將分區表之間Join操作優化為Partition-Wise Join來做計算下推。但是,當分區表的拓撲發生變更後,例如,在水平擴展中,分區會經常發生了分裂、合併或遷移後,原本分區方式完全相同的兩張分區表,就有可能出現分區方區不一致,這導致這兩張表之間的計算下推會出現失效(如下圖所示),進而對業務產生直接影響。因此,與CockroachDB相比, PolarDB-X 創造性地通過引入表組與分區組來解決這類問題。
在PolarDB-X中,如果有多個表都採用了相同的分區策略,那麼,它們在邏輯上會被PolarDB-X劃分為一個組,稱之為表組(Table Group)。表組內各個表所對應的相同分區的集合,也會被劃分成一個組,叫分區組(Partition Group)。因此,若兩張表是屬於同一個表組的,PolarDB-X可認為它們採用了完全一致的分區策略,這兩張表的各個分區所處的物理位置可以被認為完全相同。這樣優化器就會根據兩張表是否屬於表組來判斷是否做計算下推。
基於上述定義的分區組,PolarDB-X 只要約束所有的分區變更必須要以分區組為單位(即分區組內的各分區,要麼同時遷移、要麼同時分裂),即可保證在水平擴展的過程中,PolarDB-X 的計算下推不受影響。後文為闡述方便,如無特別說明,所有對分區的操作,默認都是指對分區組的操作。
當引入了表組與分區組後,PolarDB-X還可以額外地滿足用戶對容災隔離、資源隔離等場景的需求。因為表組與分區組本質上是定義了一組有強關聯關係的表集合及其分區的物理位置信息,所以,同一分區組的分區集合所處的DN節點必然相同。例如, 用戶可以採用按用戶的ID進行LIST分區建表,然後通過指定分區組的物理位置信息(PolarDB-X支持修改分區組的位置信息),將業務的大客戶/重要客戶單獨的數據劃分到更高規格更可靠的DN節點,來實現資源隔離。
水平擴展流程
介紹完PolarDB-X的架構與分區策略後,我們開始介紹PolarDB-X的水平擴展。從效果來看,水平擴展的最終目標,可歸結為兩個:系統沒有明顯熱點,各分區負載均衡;系統的處理能力與所增加的資源(這裡的資源主要是DN節點)能呈線性的增長。前邊分析的 CockroachDB 的例子, CockroachDB 在做水平擴展過程中,系統的負載均衡其實是通過多次主動的分裂分區或合併,生成多個Range, 然後再通過Leader調度,將各Range的LeaderHolder (負責讀寫流量的Range) 均衡地散到新加入的CockroachDB節點中來實現。
事實上,CockroachDB 的水平擴展的過程, 放在 PolarDB-X 也是同樣適用的。當用戶通過 PolarDB-X 默認的一致性Hash預建一些分區後, 它的均衡調度器在內核也會通過多次的分區分裂或合併,來讓各分區達到相對均衡的狀態(因為即使按默認一致性Hash分區,用戶的業務數據本身或訪問流量在各分區也可能是不均衡的),達到沒有現明顯的熱點分區。這時,當有新加DN節點加入時,均衡調度器就可以將一部分分區的流量通過分區遷移的方式,將它們切到新DN節點上,從達到擴展的效果。所以,PolarDB-X 的水平擴展,從大體上流程會有以下幾個步驟:
- 加入節點。系統添加一個新的空的DN節點;
- 分區調度。均衡調度器決定需要遷移到新DN節點的分區;
- 分區變更。執行分區遷移任務(這個過程可能同時還伴隨有分區分裂或分區合併的操作);
- 流量切換。被遷移的分區的流量切換到新DN節點,達到負載均衡狀態。
整個過程的實現要依賴到的分區變更操作有3種:分區遷移、分區分裂與分區合併,這一點與 CockroachDB 類似。分區遷移主要用於解決不同DN節點之間負載不均衡的問題,而分區的分裂與合併則可用於解決不同分區之間的負載不均的問題以及因分區數目不足水平擴展受限的問題。下邊我們繼續關注 PolarDB-X 這3種分區變更操作是如何實現。
基於 Online DDL 的分區遷移
分區遷移通常需要兩個步驟分區複製與流量切換兩個階段。像 CockroachDB 基於 Raft 協議來完成跨節點間的數據複製以及 Leader切換(流量切換),所以它的分區遷移能幾乎全被封裝Raft協議裡進行。但是,PolarDB-X 跨DN節點之間的分區複製,並沒有基於 Raft/Paxos 協議 ,而是參考 SAP HANA 提出的非對稱分區複製[2] 的思路,採用了 Online DDL [1] 的方案。
更具體來說,就是 PolarDB-X 會將分區副本的數據複製操作,看作是一次給主表(源端DN節點)添加一個特殊索引(目標端DN節點)的DDL操作,這個特殊索引會冗餘主表所有的列。所以,整個分區複製的過程就等價於基於 Online DDL 添加一次索引。類似地,PolarDB-X的流量切換沒有藉助走 Raft/Paxos 協議的切主來進行切流,而是採用基於 Online DDL 刪除索引的方式來完成流量從源端(老DN節點)到目標端(新DN節點)的對應用近乎透明的平滑切換,這個過程在PolarDB-X中被稱為透明切換。
如下圖所示,組成 PolarDB-X 分區遷移的 分區複製 與 透明切換 兩個關鍵階段。
下邊將會詳細介紹PolarDB-X 是如何基於 Online DDL 來實現分區複製與透明切換,以及最終達對用戶透明的效果。這裡邊會涉及 Google F1 論文《Online, Asynchronous Schema Change in F1》一些細節,建議讀者可以先讀下PolarDB-X 的這篇文章《PolarDB-X Online Schema Change》瞭解 Online Schema Change中原理。
分區複製
分區複製的任務是要在目標DN節點上構建一個新的分區副本。藉助 Online Schema Change,該階段會對目標分區進行一次添加“索引表”的操作,完成後該 “索引表” 便成為新的分區副本。所以,分區複製的DDL任務狀態與添加索引的類似,分為 Absent、DeleteOnly、WriteOnly、 WriteReorg 與 ReadyPublic 5個狀態(如下圖所示)。
前三個狀態 Absent、DeleteOnly、WriteOnly 的定義與添加索引的完全一致,新引入的兩個狀態 Write Reorg 與 Ready Public 的定義如下:
- WriteReorg。處於該狀態的節點的行為與 Write Only 完全一致,即要負責維護主表(即原分區)所有的增量數據及其索引表(即新分區副本)數據的一致性。可是,當節點處於該狀態時,說明 GMS 正在執行數據回填(BackFill)任務來為主表的存量數據補充其索引數據。
- ReadyPublic。處於該狀態的節點的行為與 Write Only 也完全一致。可是,當節點處於該狀態時,說明 GMS 的 BackFill 任務與相關的數據校驗工作已經完成,此時節點會認為當前主表與索引表的數據已完全一致,可隨時進入下一階。
◇ 分區複製的DDL任務狀態新引入上述兩個狀態,目的有兩個:(1)數據回填任務通常運行時間比較長(從幾分鐘到幾小時不等),通過將數據回填任務單獨抽象為 WriteReorg 狀態,可方便CN節點的SQL引擎能針對該狀態下的DML的執行計劃做一些優化工作;(2)當完成數據回填任務後,目標分區其實並不需要真的像索引表那樣要對外開放檢索,而是需要馬上進入下一階段,因此,需要 ReadyPublic (與Public區分)這個狀態來讓節點知道當前主表與索引表的數據已經完全一致,達到進入下一階段的要求。
透明切換
透明切換的任務是要將原分區的讀寫流量全部切換到新的分區副本,並且要全程對應用保持透明。所謂保持透明,就是在整個切換過程中,不能產生讓應用感知的報錯,不能讓應用的讀寫流量受影響,不能讓數據產生不一致。那常見的流量切換方案,例如,直接切流、停寫後再切流等方案,是否能滿足“透明”的要求呢?我們可分情況討論。
如果採用直接切流的方案(上圖左側),那切換期間分佈式系統中便會因為節點狀態不兼容(因為所有節點不可能在同一時間完成切換)而導致出現數據不一致;如果採用停寫後再切流的方案(上圖右側),那需要通知分佈式系統所有節點來阻塞業務所有的寫操作,但通知過程本身時間不可控(或網絡故障或節點自身不可用),應用有可能導致被長時間阻塞寫,從而使應用側會產生超時異常。可見,無論是直接切流還是停寫後再切流的方案,都不能滿足保持透明的要求。
PolarDB-X為了實現透明的切換效果,其思路是將目標端分區看作是主表,將源端分區看作是索引表,並對主表進行一次標準的刪除索引的 Online DDL 操作(其狀態過程是 Public-->WriteOnly-->DeleteOnly-->Absent ,與 Online Schema Change 定義的一致)。
如上圖所示,在 Online DDL 期間,由於新索引表會被逐漸下線,CN節點的SQL引擎根據不同DDL狀態,將不同類型的流量分步驟地從源端(索引表)切換到目標端(主表):先切Select流量(WriteOnly狀態,索引表不可讀),再切Insert流量(DeleteOnly狀態,索引表不可插入增量),最後才切Update/Delete/加鎖操作等流量(Absent狀態,索引表不再接收寫入)。基於 Online DDL ,整個切換會有以下幾點的優勢:
- 切期期間業務讀寫不會因被阻塞(不需要禁寫);
- 切換期間不會產生死鎖(因為在WriteOnly與DeleteOnly狀態的節點,加鎖順序都是一致);
- 切換期間不會產生數據不一致的異常(Online Schema Change 論文已證明)。
這些優勢使得流量切換全程能對應用做到了透明。
多表並行與流控
前邊說過,為了保持計算下推,PolarDB-X 的分區遷移其實是以分區組為來單位進行。一個分區組通常會有多個不同表的分區,所以,分區遷移過程產生的數據回填任務,通常是將分區組內的多個分區進行並行回填,以加速遷移的速度。
回填任務是一個比較消耗時間與消耗資源的後臺操作,涉及源端到目標端的大量數據複製,如果其速率太快,容易過多地佔用DN節點的 CPU 與 IO 資源,對業務的正常讀寫造成影響;如果其速率太慢,分區遷移過程的運行時間又可能會太長。因此,在Online DDL 的框架下,數據回填任務會支持進行動態流控:一方面允許按人工介入來調整任務的狀態(例如暫時任務),另一方面可以根據節點的資源情況來動態調整數據回填速率,以避免對業務產生過多影響或遷移過程太慢的問題;
透過數據回填的流控例子,PolarDB-X 基於 Online DDL 的分區遷移方案,相比於 CockroachDB 等基於 Raft 協議做一致性複製的方案,雖然增加了一定的實現複雜度,但卻可以帶了更靈活的對用戶更友好的可控性。
基於 Online DDL 的分區分裂
PolarDB-X 分區 與 CockroachDB 分區 的存儲方式有一定的差異:同一 Cockroach Node 中不同 Range 只是邏輯上的劃分,物理存儲是共用一棵 RocksDB 的 LSM-Tree;而 PolarDB-X 中同一DB節點的不同分區實際上是對應著的是 X-DB 中的不同的表(即MySQL中的一張表),每張表都是一棵 B+Tree,它們之間的物理存儲是分開的。因此,PolarDB-X 的 分區分裂本質上是將 InnoDB 的 B+Tree 由一棵拆分為兩棵的過程,這個過程中間必然需要涉及到數據的複製與重分佈。諸如 CockroachDB 僅通過修改 Range 元數據來完成分區分裂的做法,對於 PolarDB-X 的分裂操作將不適用。考慮到分裂過程中數據複製與重分佈,PolarDB-X 的分區分裂同樣是採用 Online DDL 的實現方式。套用 Online DDL 的框架,PolarDB-X 的分區分裂其實可看成是對分區遷移的一種擴展。回顧分區遷移的運行過程:
- 首選,將源端分區看作主表,將目標端分區看作是主表的索引表,並通過一個添加索引的 Online DDL 操作實現從源端分區到目標端分區的數據複製;
- 然後,重新將目標端分區看作是主表,將源端分區看作是索引表,再通過一個刪除索引的 Online DDL 操作實現讀寫流量從源端分區切換到目標端分區。
在這過程中,源端分區與目標端分區一直被當作獨立主表與索引表來處理,主表與索引表本身的分區方式是否相同,不會對整個模型的運作產生影響。因此,在分裂場景中,若將分裂後的兩個新分區看作一個分區索引表(即該索引表有兩個分區),而該分區索引表依然可以看作是源端分區的一個副本。這樣,分區分裂便可以繼續套用分區遷移的全部流程,其中需要擴展的地方就是所有產生對分區索引的Insert/Update/Delete操作都要要再經過一次分區路由(Sharding)以確定要修改真實分區。於是,基於這個思路,分區分裂就可分下幾個階段來完成:
- 準備分裂後的分區。在此階段,PolarDB-X GMS 找兩個空閒的DN節點, 根據分裂點(Split Point)將原分區的取值空間劃分成兩個部分,並在空閒的DN節點上構建這兩個新的空分區。
- 帶分裂的分區複製。該階段與分區遷移的分區複製類似,不同的是所有源於主表修改而產生的對分區索引表的修改都需要先經過一次分區路由,以確定其實際的修改位置(如上圖所示)。
- 帶分裂的透明切換。該階段與分區遷移的透明切換類似,不同的是所有切到分區索引表的讀寫流量都要先經過一次分區路由,以確定其實際的修改位置(如下圖所示)。
- 數據清理。該階段與分區遷移數據清理類似,需要將被分裂的分區數據進行清理以及相關元數據的刷新。
PolarDB-X 的分區分裂由於基本複用了分區遷移的流程。因此,分裂過程也是以分區組為單位進行,分裂前後 PolarDB-X 的計算下推不會受影響。
總結與展望
基於 Share-Nothing 架構的 PolarDB-X 通過對數據進行水平分區,解決了單機數據庫寫能力無法水平擴展的問題。每個分區基於 Paxos 協議實現多副本的強一致複製,並支持分區級的高可用、錯誤容災與利用多副本提供讀擴展的能力。PolarDB-X 支持通過動態增加數據節點實現存儲層的水平擴展與自動負載均衡。但是,為了避免水平擴展過程中的負載不均衡,引入了分區遷移與分區分裂兩項必須的基本能力。
分區遷移能解決數據節點之間的負載不均衡的問題。可進行分區遷移之前必須先做分區複製,但以 X-DB 作為存儲的PolarDB-X,暫時無法藉助 Paxos/Raft 協議實現跨機(跨數據節點)的分區複製與分區流量切換。因此,PolarDB-X 通過複用基於 Online DDL 的添加索引的過程來完成對應用透明的跨機分區複製。更進一步,PolarDB-X 還將 Online DDL 的機制推廣到跨機分區遷移,將通過複用刪除索引的 Online DDL 過程來解決了分區遷移過程讀寫流量從源端到目標端的切換問題。PolarDB-X 基於 Online DDL 的分區遷移可以做到全程對應用無感知,遷移過程的回填任務支持流控,避免資源佔用過多並對應用產生影響。
分區分裂能解決分區之間的負載不均衡的問題。可是 PolarDB-X 的每一個分區對應一張物理表,分裂過程要將一張表拆分多張表,因此,該過程需要涉及到對分區的數據複製與重分佈。鑑於要涉及分區的數據複製,PolarDB-X 的分區分裂同樣是採用 Online DDL 的實現方式,其思路是通過擴展分區遷移的模型:分區遷移是通過複用添加索引的DDL過程完成分區複製,而分區分裂則是將添加索引的操作擴展為添加分區索引,以解決分裂過程中涉及的數據重分佈問題。
通過分區分裂,PolarDB-X 能產生更多分佈在不同數據節點的分區;通過分區遷移,PolarDB-X能均衡不同數據節點間之間負載,如此往復,最後達到水平擴展的效果。
與諸如 CockrocachDB等主流的NewSQL數據庫相比,PolarDB-X 的水平擴展方案帶有與自己架構緊密相關的特色,以下是 PolarDB-X 水平擴展與其它 NewSQL 數據庫 的對比。
後記
分佈式數據庫中,水平擴展通常與分區管理、負載均衡方面緊密結合。本文限於篇幅,暫時只對PolarDB-X 水平擴展方面作了一些原理性的介紹,像動態分區管理、自動負載均衡(如分區物理位置的選擇、分裂點的選擇、如何分區做遷移等)的一些細節沒涉及太多說明,請讀者關注後續的文章。
參考文獻
- [1] Online, Asynchronous Schema Change in F1.
- [2] Asymmetric-Partition Replication for Highly Scalable Distributed Transaction Processing in Practice.
- [3] Elastic Scale-out for Partition-Based Database Systems.
- [4] TiDB: A Raft-based HTAP Database.
- [5] CockroachDB: The Resilient Geo-Distributed SQL Database.
- [8] In Search of an Understandable Consensus Algorithm.
- [9] What’s Really New with NewSQL.
- [10] Spanner: Google's Global-Distributed Database.
- [12] Comparison of Different Repliacation Solutions.
- [13] MySQL Group Replication.
- [14] Paxos Made Simple
- [15] In Search of an Understandable Consensus Algorithm
- [16] YugabyteDB: Automatic Re-sharding of Data with Tablet Splitting
- [17] MySQL:Replication for Scale-Out