天天看點

資料庫核心那些事|分布式資料庫,挂掉兩台機器會發生什麼?

作者:阿裡雲瑤池資料庫

挂一部分機器,不會丢資料、不會不可服務,是對現代資料庫的一個比較基本的要求。

對于早期的單機資料庫,一般使用主備架構。主備架構有很多的缺陷,并且這些缺陷是無解的。穿過主備架構裡各種“優化”的名詞,最後也無非是選擇一碗毒藥而已,這幾個毒藥包括:

  1. 腦裂,兩個節點同時寫入的沖突資料無法合并,隻能丢掉一部分。想要不腦裂?那隻能犧牲可用性。
  2. 同步複制,備機不可用的情況下,算不算寫入成功?算,可能丢資料;不算,備機不可用==叢集不可用,犧牲可用性。
  3. 異步複制,這完全躺平了,不考慮一緻性。
  4. 所謂semi-sync等方案,也屬于主備架構的一種。
  5. 業務自己去容錯,做針對自己業務場景的對賬、補償等方案。

其實可以看出,主備架構是CAP理論做取舍的重災區,一緻性和可用性之間的關系特别沖突。所謂一緻性和可用性“兼顧”的主備方案,實際上是“兼不顧”。

最佳實踐:在這個時代,但凡資料有一定的重要性,都不應該選擇主備架構的産品。

分布式資料庫,除了擴充性之外,解決傳統資料庫主備結構的容災問題也是其主要任務。Paxos\Raft(包括其他變種協定)成為了主流選擇。其共通點是,每份資料會存在三個副本,并且能夠保證在一個副本挂掉的情況下,不影響可用性,并且不會出現任何一緻性問題(腦裂、丢資料、丢更新等)。

本文無意去解析Paxos\Raft協定,此類文章已多如牛毛。但有個疑問是,是否一個資料庫隻要使用了Paxos\Raft協定,那就一定是安全穩定可靠的呢?

我們将探讨幾個問題:

  1. 除了協定本身,還有什麼因素影響分布式資料庫的可用性?
  2. 如何計算不同架構的分布式資料庫的可用性?
  3. KV層的可用性和關系型資料庫的可用性是否等價?
  4. 資料庫的可用性和應用的可用性是否是等價的?

從1+1=2說起

我們先從一個最簡單的例子說起。假如有以下的資料庫結構:

資料庫核心那些事|分布式資料庫,挂掉兩台機器會發生什麼?

請問,當挂一個節點和兩個節點的情況下,可用性分别是?

顯而易見,挂一台機器的情況下,可用性是100%;挂兩台機器的情況下,可用性是0%,所有資料都不可用,如果這兩台機器被徹底的摧毀,那代表所有資料都丢失了。

排程就是一個填圖遊戲

假如我們有6台機器,我們想将資料庫部署在這6台機器上,進一步提升擴充性和容災性:

  1. 為了提升擴充性,我們會對資料進行分片
  2. 每個分片需要存在三個副本
  3. 為了保證容災,每個分片的三個副本需要配置設定在不同的機器上

假如我們把資料切成了12個分片,共有12*3=36份副本:

資料庫核心那些事|分布式資料庫,挂掉兩台機器會發生什麼?

下面我們來做一個填圖遊戲,将上面的副本填充到下面的6台機器中(需滿足上面的限制,并且每台機器配置設定到6個副本),你能想到多少不同的填充方式:

資料庫核心那些事|分布式資料庫,挂掉兩台機器會發生什麼?

模型1,完全的随機排程

資料庫核心那些事|分布式資料庫,挂掉兩台機器會發生什麼?

我們均勻的将12個leader副本分散在叢集中,每個節點配置設定到兩個leader副本,除了這兩個限定條件,分片與分片之間的排程是無關系的。

目前市面上常見的使用分布式KV+SQL的分布式資料庫,一般使用的都是此類模型。

這種情況下,如果挂兩台機器,我們分析下會是什麼樣的情況。

例如,節點1和節點2同時當機,由于p1、p6均有兩個副本在這兩個節點上,是以p1和p6處于不可用的狀态。

我們枚舉一下可能出現的情況:

資料庫核心那些事|分布式資料庫,挂掉兩台機器會發生什麼?

一個比較直覺的事實:在此模型下,任意挂兩台機器,均會導緻部分資料不可用。

設分片數不是12而是M,機器數不是6而是N,我們做幾個簡單的計算:

1. 任意挂兩台機器,不可用的分片數的期望值是:

簡單的推導過程:

對于特定的一個分片,挂一個副本的機率是 , 再挂一個副本的機率是。是以,特定的一個分片挂掉的機率是

M個分片中挂掉的數量乘以M便是

将M=12,N=6帶入,不可用的分片數的期望值是,和我們上面列的具體表格是相符的。

2. 任意挂兩台機器,完全可用或者不丢資料(不存在不可用或者丢資料的分片)的機率為:

簡單的推導過程:

對于一個特定分片,其可用的機率是,M個分片均可用的機率是其M次幂。

将M=12,N=6帶入,完全可用的機率是%。

3. 正常情況下,機器數越多,資料量越大,分片數越多,M和N會成一個比例,單節點上的副本數量記為m,則:。任意挂兩台機器,完全可用的機率為:

以一個N=50節點的叢集為例,我們看一下m的取值與可用性的機率關系:

資料庫核心那些事|分布式資料庫,挂掉兩台機器會發生什麼?

可以直覺發現,可用性會随着m的增長,迅速下降。當m=1(每個機器隻存一個副本的情況下),可用性最高,達到96%。

什麼情況下m會變大?

如果設計資料庫時,将單分片的大小限定的比較小,例如單個分片96M,那麼m就容易非常大(50個96M大約隻有5個G的資料,每個節點隻存100G的資料,也有1000個分片了)。

模式1下,各分片(或者說各Paxos協定組)的副本分布缺乏協調,如果單節點分片數略多,隻要同時挂兩台或者更多機器,幾乎一定會有一部分資料不可用或者丢失。

資料可用性!=業務可用性

回到上面的例子中來,12個分片中,有2.4個不可用,好像也可以嘛,畢竟挂了33%的機器嘛,是不是隻影響了20%的業務?

我們需要明确的是,20%的不可用,僅僅的指資料層面的。例如我們把它想象成一個KV資料庫,那麼确實,對于這個KV資料庫來說,“隻有”20%的資料不可用。

但!資料的可用性和業務的可用性很多時候是不等價的!

第一個點顯而易見,不可用的背後,一種情況是資料丢失,對于有的業務來說(例如存銀行的賬戶餘額),無論丢失的比例有多麼的低,都是無法接受的。對于此類業務,丢失的機率必須無限趨近于0。

第二個點則更為隐晦。

我們知道,絕大多數業務,不會直接通路KV資料庫的。一般情況下,我們會在KV之上建構一個關系型資料庫。關系型資料庫比KV資料庫多了兩層概念,表和索引。從業務角度來看,一次業務請求,會操作多個表的多個索引。

例如,業務接到某個HTTP請求後,會執行這樣一段操作:

begin;
## 根據user_id上的索引查詢一下使用者的餘額,并且這個查詢還需要對主鍵進行一個回表(涉及到2個key)
select * from user_info where user_id=xxx; 
## 按照item_id對庫存進行扣減(涉及到1個key)
update items set count=count-1 where item_id=xxx
## 向訂單表orders中寫入一條記錄(注意,orders表還有5個二級索引,是以等于要寫6個key)
insert into orders xxx;
commit;           

這次HTTP請求的進行中,一共涉及了三張表的9個索引(主鍵或二級索引)的9個key,并且這9個key之間沒有任何的關聯關系,也就是說,他們是均勻的分布在叢集中的。

雖然每個key(也即key所在的分片)的可用性“高達“80%,但指數是一個可怕的東西,它會放大你所有的壞運氣,9個key運氣都很好,全部可用的機率隻有%。

也即,從業務角度看,HTTP請求的成功率隻有13%,失敗率87%。87% vs 20%,這就是業務最終看到的不可用性與KV層看到的不可用性。

兩個結論:

  1. 關系模型(表、索引)的引入,會指數放大KV層的不可用性
  2. 一般業務會在一次請求内操作多個表、索引、Key,會繼續指數放大KV層的不可用性

模型2,分片之間存在一定的綁定關系的排程

在模型1中,m的值上升後,由于指數關系,可用性會迅速下降。如果M僅與節點數N相關,而與單節點副本數m無關,則能将可用性維持在一個比較好的數字上。

資料庫核心那些事|分布式資料庫,挂掉兩台機器會發生什麼?

在模型2中,我們将節點每三個組成一個組,每個組的每個節點,所包含的副本來自相同的分片,如上圖所示。p1-p6、p7-p12各屬于一個組。我們枚舉一下可能出現的情況:

資料庫核心那些事|分布式資料庫,挂掉兩台機器會發生什麼?

計算下,任意挂兩台機器,完全可用或者不丢資料(不存在不可用或者丢資料的分片)的機率為:

簡單推導過程:

對于一個特定的組,其可用的機率是,N/3個組均可用的機率是其N/3次幂。

資料庫核心那些事|分布式資料庫,挂掉兩台機器會發生什麼?

我們可以看到,随着機器數的增加,可用性會迅速增加,20個節點的時候便能超過90%。

模型2相對于模型1,不同分片(或者說不同Paxos協定組)之間的排程有了一定的綁定關系,使其變成了僅與節點數成線性關系的量,顯著的提升了可用性。

資料庫核心那些事|分布式資料庫,挂掉兩台機器會發生什麼?

注意,這裡說的“組”,不是平時說的Paxos組、Raft組之類的概念,它是一種排程上的概念。一個直覺的描述,節點、“組”、Paxos\Raft組、分片之間的關系:

  • 節點與“組”:3對N,N>=1,也即1個“組”包含3個節點,一個節點可能屬于多個“組”
  • “組”與Paxos\Raft組:1對N,N>=1
  • Paxos\Raft組與分片:通常情況下是1對N,很多資料庫實作是1對1

在有“組”概念的資料庫裡,它有不同的名字。例如對于PolarDB分布式版來說,他對應的是“DN”這個名字,每個DN擁有三個節點(三個程序),每個DN有一個Paxos組,每個DN(Paxos組)有若幹分片。

表組&本地索引

模型2很大程度上降低了單分片故障的風險,但是,上文中我們說過,業務請求中的多個事務、多個表、多個索引,都會放大這個不可用性。對于穩定性要求特别高的業務,分布式資料庫需要提供更多能力來進一步降低此類風險。

PolarDB分布式版使用表組技術來解決此類問題。其核心思路是,将同一次業務請求中涉及到的資料,盡量綁定在一起,排程到相似的位置上,這樣其可用性不會受涉及到的資料量指數影響。

例如,某業務有使用者這個概念,那麼在PolarDB分布式版中,可以使用分區表、表組、本地索引等工具,将一個使用者涉及的多個表(例如使用者詳情、記錄檔、位址清單等)、多個記錄、多個索引綁定在同一個DN上。

如我們在《PolarDB-X 資料分布解讀(四)》[1] 中所說,這實際上是資料庫手動模式的一個應用。資料庫需要提供更多的工具來達成核心業務對穩定性和資料安全性的要求。

自建 vs 雲服務

在雲上,自建的分布式資料庫會存在一些額外的問題。

雲上自建,通常情況下,我們會購買一批的ECS,在ECS上部署分布式資料庫。

這裡一個很大的風險點是,ECS都是虛拟化的(除非購買獨享實體機的那種超大規格),一台實體機上會虛拟化出多台ECS,而作為普通的雲使用者,是無法感覺到ECS的排程政策的。

例如,多個節點,甚至同一份資料的多個副本所在的ECS會在一台實體機上:

資料庫核心那些事|分布式資料庫,挂掉兩台機器會發生什麼?

這種節點的實體分布,自建資料庫是無法感覺,也無法控制的。

此外,自建還容易出現如下問題:

  1. 同一個實體機上,多個資料庫節點的IO、網絡帶寬争搶等問題
  2. 買到的同CPU記憶體規格的ECS,可能對應不同的CPU型号,性能之間存在差異

以上問題,會極大的增加雲上自建分布式資料庫的故障風險和機率。

通常情況下,有以下幾種解決方式:

1. 三可用區部署。由于不同可用區的ECS肯定在不同的實體機上,通過資料庫内部的locality等能力,可以確定對于同一個分片的三個副本不在同一台實體機上。缺點是:

a. 依然無法控制同可用區内的實體分布情況

b. 多可用區帶來的響應時間的變化

2. 購買獨立的實體機規格的ECS。完全自己運維實體機内部的虛拟化、隔離、節點分布等。缺點是:

a. 貴

b. 運維要求很高

c. 對于機架、交換機等依然無法控制

雲廠商提供的分布式資料庫服務則能很好的解決此類問題。例如雲上的PolarDB分布式版服務會做到以下幾點:

  1. 同一個叢集的所有節點一定都在不同的實體機上、不同的機架上;
  2. 同一個叢集的所有節點使用的實體機的CPU等型号都是一緻的。

雲上自建資料庫,會面臨更多的穩定性風險。雲上自建分布式資料庫,此類風險會指數上升。

最後

模型1由于看起來非常的幹淨、很容易做到分層清晰(每個分片可以自由排程,也不用考慮索引之間的位置關系),特别适合做一些資料庫領域的玩具級項目。但用在關系型資料庫(因為大量的表、二級索引會急劇放大這種效應)中就是徒有其表,在面臨多機故障的情況下,其不出問題的機率會随着資料量和節點數的增長迅速降到0。

模型2則更适合生産級的資料庫使用。如果讀者要使用分布式資料庫,建議仔細甄别其是否有類似模型2的排程政策。

表組、本地索引等能力也是生産級資料庫不可或缺的。這些能力的合理使用, 會避免指數級放大不可用性。對于穩定性要求更高的應用,需要選擇有這些能力的資料庫。

現在我們可以回答文章開頭的幾個問題了。

1. Paxos\Raft協定是根本,協定上有問題基本跟資料安全就沒啥關系了;

2. 隻有協定也是萬萬不行的,如果缺少有綁定關系的排程政策、表組、本地索引等能力,都會極大的影響最終業務層看到的可用性;

3. 關系模型、業務對資料庫的使用邏輯等,都會成指數的放大KV層的不可用性。

歡迎大家持續關注,我們會在後續的文章裡,帶來更多生産級資料庫在設計與方案選擇上的一些思考。

參考文章

[1] PolarDB-X 資料分布解讀(四):透明 vs 手動:https://zhuanlan.zhihu.com/p/556788150

推薦閱讀

PolarDB-X核心新版本:更精細的資料管理

三款典型國産分布式資料庫的對比評測

PolarDB-X開源分布式資料庫在韻達科技的應用實踐

繼續閱讀