分布式數據庫系統在邏輯上可以看作一個完整的系統,用戶如同在使用單機數據庫系統;但是,從物理角度看,其為一個網絡系統,包含若干個物理意義上的分散的節點,而節點之間通過網絡進行連接,通過網絡協議進行數據交換。
分布式數據系統需要應對網絡故障、節點故障。網絡故障會直接導致分區事件(CAP
原理中的P,即網絡發生故障使得網絡被分為多個子部分)發生,系統的可用性會受到影響;節點故障可能會引發單點故障,也就是在數據為單副本的情況下節點故障會直接導致部分數據不能被訪問。為避免單點故障,數據需要有多個副本,從而使系統的可用性得到較大提高。節點故障也可能引發分區事件。
除了上述問題外,分布式數據庫系統還可能帶來不一致問題。比如舊讀(stale read)問題,即讀操作發生于數據項更新之后,此時本應該讀取到的是該數據項的最新值,但是卻讀到了舊值。產生該問題的原因是,分布式數據庫系統沒有一個統一的時鐘,這會導致反序讀取數據的情況出現。這種情況在單機系統中是不存在的。這里所說的不一致現象,以及與其類似的不一致性現象,在這里稱為數據讀取序不符合數據生成序,簡稱分布式不一致。
為了解決分布式不一致問題,諸多學者經過大量的研究提出了多種分布式一致性的概念,如線性一致性(linearizability)、順序一致性(sequential consistency)、因果一致性(causal consistency),以及Google Spanner的外部一致性(external consistency)等。
分布式數據庫系統需要解決分布式不一致問題,使觀察者能讀取到滿足一致性的數據,以確保數據之間的邏輯一直是有序的。本節后續內容將針對這個問題展開討論:首先討論通用的分布式系統所面臨的問題,然后討論因數據異常引發的一致性問題,最后討論與分布式數據庫相關的其他問題。
分布式數據庫系統面臨的問題
單機數據庫系統為了應對事務故障和對事務進行管理,專門提供了UNDO日志、回滾段等措施,目的就是實現事務的回滾;為了應對系統故障,采用了WAL技術做日志,目的是先于事務進行持久化存儲;為了應對介質故障,專門提供了邏輯備份、物理備份等多種手段,目的是在數據層面、日志層面和物理數據塊層面實現數據冗余存儲。
相對于單機數據庫系統而言,除了上述問題外,分布式數據庫系統面臨著更多的挑戰。這些挑戰源自分布式數據庫系統的架構,其和單機數據庫系統不同,因而在技術層面上存在差異。
1. 架構異常
架構異常是指用戶因數據庫的架構而產生的數據異常,嚴格地講,這不屬于數據庫系統領域的數據異常。從用戶的角度看,事務一直在執行中,但是讀寫數據時產生了類似前述的順序問題、數據異常等,本書統稱這種異常為架構異常。架構異常和分布式架構相關,分布式架構包括一主一備架構、一主多備架構、多主多備架構等。在分布式架構中,前端可能都有一個類似代理(proxy)的組件面向用戶提供透明的高可用服務,代理組件屏蔽了后端多個單機系統故障,所以在用戶看來,分布式架構上的所有操作都是在一個事務中進行的,而因架構引發的異常也是數據異常。
如下討論一種已知的架構異常,該架構異常會導致讀取到的數據不一致。我們以MySQL的主備架構Master-Slave為例進行說明(其他數據庫的同類架構存在類似隱患)。此類不一致是這樣產生的。MySQL支持Master-Slave架構。假設在Master上執行事務T,此時先按條件“score>90”進行查詢,發現沒有符合條件的事務,故成功寫入Binlog File的數據,假設其為95(事務提交),然后在復制的過程中宕機,導致復制失敗。Master重啟時,會直接對數據95進行提交操作,之后Master會將數據95異步復制到Slave。但是,此時原來的Slave可能已經切換為主機并開始提供服務,比如新事務寫入數據98,而原來Master上的95沒有被復制到新Master上,這就會造成兩臺MySQL主機的數據不一致。
如果在主備MySQL服務前端還有一個代理服務器,對用戶而言,這會屏蔽后臺的主備服務,用戶就會認為“只有一個MySQL”提供服務,因此數據95丟失對用戶而言是不可接受的。
還有一種情況,如果代理服務器在原始的Master宕機后沒有結束用戶的事務T,而是把事務T連接到原備機,并將原備機變更為新Master。這時,對于新Master而言,會發生兩個事務,一個新事務T1在一定WHERE條件下寫入98,另一個是繼續執行的原事務T,若此時原事務T再次發起讀操作(邏輯上還在同一個事務內),就會發現自己寫過的數據95消失了,這對于用戶而言是不可接受的。從分布式一致性的角度看,這違背了“Read-your-writes”(讀你所寫)原則。從事務的角度看,可能出現“幻讀”,即再次按條件“score>90”查詢,額外讀到事務T1寫入的98,所以出現了事務的數據異常。
與上述相似,官方對MySQL上出現Master-Slave之間數據不一致的情況,也進行了描述。
如下圖1所示,如果把數據擴展到多副本,把讀操作擴展到允許從任何副本讀取數據,把寫操作擴展到允許向任何副本寫入數據,如果是去中心化的架構(即沒有單一的全局事務管理機制)且發生了網絡分區或延,則在事務一致性視角、分布式一致性視角下去觀察數據的讀或寫操作,會發現存在更為復雜的問題。
圖1 多副本異常圖
Distributed algorithms and protocols討論了一種在多副本情況下,副本間數據同步與數據可見性的異常情況,其所用的示例如圖1所示:足球世界杯比賽結果出爐,比賽結果經過Leader節點記錄到數據庫。事實結果是德國贏得了世界杯冠軍。但是,數據從Leader節點同步到兩個不同的Follower節點的時候,Alice和Bob同處一室,從不同的Follower節點上查詢世界杯的比賽消息,結果Alice得知德國奪冠,而Bob卻得到比賽還沒有結束的消息。二人得到了不同的消息,產生了不一致。這也是分布式架構下因多副本支持Follower讀帶來的不一致的問題。
2. 分布式一致性和事務一致性
為了幫助大家充分理解分布式系統中存在的問題,我們不妨做一個類比。
若是世界上只有一個人,那么這個世界的關系是非常簡單的,但是一旦有多個人,“社會”就會形成。其中,社會關系指的就是人與人之間建立的關系,這種關系會隨著人的數量的增加而不斷復雜化。這種復雜的社會關系與數據庫結合到一起得到的就是分布式數據庫系統,社會中的人就相當于分布式數據庫系統中的一個物理節點或者一個物理節點中的一份數據副本。圖2以一個NewSQL系統的架構為例描述分布式數據庫中存在的多個問題。
因為分布式數據庫要存儲海量數據,要對數據分而治之,所以引入了數據分片的概念。從邏輯的角度看,每個節點的數據都是一個或多個數據分片,但是數據庫要滿足“高可用、高可靠”以及在線實時提供服務的特性,因此每個數據分片就有了多個副本。數據多副本使得分布式數據庫的“一致性”問題變得更為復雜。
我們從讀和寫兩個不同的角度來感性了解一下分布式數據庫中存在哪些不一致的問題。
首先,圖2所示的分布式數據庫系統存在4個數據分片—A、B、C、D,每個分片又存在3個副本,且每個分片的3個副本中有一個是Leader,另外兩個是Follower(比如Raft分布式協議中的Leader和Follower)。
圖2 分布式數據庫的一致性問題關系圖
其次,對于寫操作,圖2所示有如下兩種情況。
1)寫單個數據分片—W1:在這種情況下,一個事務不能針對多個節點進行操作,所以這樣的事務是典型的單節點事務,類似于單機數據庫系統中的事務。寫單個數據分片可以由單個節點上的事務處理機制來確保其具有ACID特性。為了實現寫單個數據分片的數據一致性,可只使用數據庫系統中的并發訪問控制技術,如2PL(Two-phase Locking,兩階段封鎖)、TO(Timestamp Ordering,時間戳排序)、MVCC(Multi Version Concurrency Control,多版本并發控制)等。
2)寫多個數據分片—W2:通過一個事務寫多個數據分片,這就是典型的分布式事務了,此時需要借助諸如分布式并發訪問控制等技術來保證分布式事務的一致性,需要借助2PC(Two-phase Commit,兩階段提交)技術保證跨節點寫操作的原子性。另外,如果需要實現強一致性(詳見5.6節),還需要考慮在分布式數據庫范圍內,確保ACID中的C和CAP中的C的強一致性相結合(即可串行化和線性一致性、順序一致性的結合)。諸如Spanner等很多數據庫系統,都使用線性一致性、SS2PL(Strong Strict 2PL)技術和2PC技術來實現分布式寫事務的強一致性。CockroachDB、Percolator等分布式數據庫則使用了OCC類的技術做并發訪問控制來確保事務一致性(可串行化),并使用2PC來確保分布式提交的原子性,但它們沒有實現強一致性,其中CockroachDB只實現了順序可串行化。保證分布式事務一致性的技術還有很多,第4章將詳細討論。
對于寫多個數據分片的情況來說,因為在每個數據分片內部存在多個副本,所以如何保證副本之間的數據一致性,也是一個典型的分布式系統一致性問題(第2章會詳細討論分布式系統的一致性問題,第3章會詳細討論多副本在共識算法加持下的一致性問題),著名的Paxos、Raft等協議就是用來解決分布式系統的多副本共識問題的。此種情況下,通常沒有寫操作會發生在圖1-6所示的A的Leader和B的Follower這樣的組合中。
如果一個系統支持多寫操作,則多寫會同時發生在多個數據分片的Leader上。
對于讀操作,圖2所示也有如下兩種情況。
1)讀單個數據分片—R1:如果一個事務只涉及單個節點,則這個事務讀取操作的數據一致性一定能保障(通過節點上的事務機制來保障)。如果涉及多個節點,那么此時的R1就會被分為R11和R12兩種讀取方式。
R11方式用于讀取Leader:因為進行寫操作時首先寫的是Leader,所以如果寫事務已經提交,那么一定能夠保證R11讀取的數據是已經提交了的最新數據。如果寫事務沒有提交,那么此時Leader上若是采用MVCC技術,則R11讀取的會是一個舊數據,這樣的讀取機制可以保證R11讀數據的一致性;Leader上若是采用封鎖并發訪問控制機制,則讀操作會被阻塞直至寫事務提交,因而在這種機制下R11讀取的是提交后的值,從而保證讀數據的一致性,換句話說,這種情況下,保證數據一致性依賴的是單節點上的事務并發訪問控制機制。同時,這也意味著一個分布式數據庫系統中單個節點的事務處理機制應該具備完備的事務處理功能。
R12的方式用于讀取Follower:讀取Follower時又分為如下兩種情況。
在一個分片內部,主副本和從副本(即Leader和Follower)之間是強同步的(Leader向所有Follower同步數據并在應用成功之后向客戶端返回結果)。這種情況下不管是讀Leader還是讀Follower,數據一定是完全相同的,讀取的數據一定是一致的。
Leader和Follower之間是弱同步的(Leader沒有等所有Follower同步數據并應用成功之后,就向客戶端返回結果),如采用多數派協議就可實現弱同步。此時Leader和Follower之間會存在寫數據延時,即從Follower上讀取到的可能是一個舊數據,但是因為事務的讀操作只涉及一個節點,所以也不會產生讀操作數據不一致的問題。這就如同MySQL的主備復制系統中備機可以提供只讀服務一樣。
2)讀多個數據分片—R2:注意這種情況下的讀操作會跨多個分片/節點,如果事務處理機制不妥當,會產生不一致的問題。而這樣的不一致問題,既可能是事務的不一致,也可能是分布式系統的不一致。下面還是以圖1-6所示為例進行介紹。假設只讀取A、B兩個數據分片,這時有如下4種情況。
讀A的Leader和B的Leader,這種情況簡稱全L問題。
- 事務的一致性:如果存在全局的事務管理器,那么此時讀多個數據分片的操作如同在單機系統進行數據的讀操作,通過封鎖并發訪問控制協議或者MVCC(全局快照點)等技術,可以確保讀操作過程中不發生數據異常。因為其他事務的寫操作會為本事務的讀操作帶來數據不一致的問題,所以通過全局的并發訪問控制協議(如全局封鎖并發訪問控制協議等技術),能夠避免出現事務層面的數據不一致問題。但是,如果沒有全局的并發訪問控制協調者,則容易出現跨節點的數據異常,所以需要由特定的并發訪問控制協議加以控制。
- 分布式系統的一致性:這類問題只在“讀A的Leader和B的Leader”這種結構中存在,分布式數據庫需要通過實現“強一致性”來規避因分布和并發帶來的分布式事務型數據系統的一致性問題。具體可能出現的問題會在第2章介紹。
讀A的Leader和B的Follower,這種情況簡稱LF問題。B的Leader和Follower之間存在時延,即傳輸存在時延,從而帶來主備復制之間的數據不一致問題。如果支持“讀A的Leader和B的Follower”這樣的方式,需要確保所讀取的節點(A的Leader節點、B的Follower節點)上存在共同的事務狀態。
讀A的Follower和B的Leader,這種情況簡稱FL問題。問題的分析和解決方法同上。
讀A的Follower和B的Follower,這種情況簡稱全F問題。問題的分析和解決方法同上。
若是在讀數據時,同時存在事務的一致性和分布式系統的一致性問題,那么就需要通過強一致性來解決。
總體來說,事務的一致性是因并發的事務間并發訪問(讀寫、寫讀、寫寫沖突)同一個數據項造成的,而分布式一致性是因多個節點分散、節點使用各自的時鐘,以及沒有對各個節點上發生的操作進行排序造成的。
本書摘編自《分布式數據庫原理、架構與實踐》,經出版方授權發布。
原文鏈接:https://mp.weixin.qq.com/s/qboZL4OWB3kZvx9B7XSG-g