分布式事務,要么全成功,要么全回滾。 和有些分布式存儲的主節點成功就返回成功,或者過半節點返回成功就返回成功,后面各節點再同步數據的方式不一樣。
原文鏈接: https://segmentfault.com/a/1190000012534071
在分布式系統中著有 CAP 理論,該理論由加州大學伯克利分校的 Eric Brewer 教授提出,闡述了在一個分布式系統中不可能同時滿足一致性( C onsistency)、可用性( A vailability),以及分區容錯性( P artition Tolerance)。
- 一致性: 在分布式系統中數據往往存在多個副本,一致性描述的是這些副本中的數據在內容和組織上的一致。
- 可用性: 描述系統對用戶的服務能力,所謂可用是指在用戶能夠容忍的時間范圍內返回用戶期望的結果。
- 分區容錯性: 分布式系統通常由多個節點構成,由於網絡是不可靠的,所以存在分布式集群中的節點因為網絡通信故障導致被孤立成一個個小集群的可能性,即網絡分區,分區容錯性要求在出現網絡分區時系統仍然能夠對外提供一致性的可用服務。
對於一個分布式系統而言,我們要始終假設網絡是不可靠的,因此分區容錯性是對一個分布式系統最基本的要求,我們的切入點更多的是嘗試在可用性和一致性之間尋找一個平衡點,但這也並非要求我們在系統設計時一直建立在網絡出現分區的前提之上,然后對一致性和可用性在選擇時非此即彼。
Eric Brewer 教授在 2012 年就曾指出 CAP 理論證明不能同時滿足一致性、可用性,以及分區容錯性的觀點在實際系統設計指導上存在一定的誤導性。 傳統對於 CAP 理論的理解認為在設計分布式系統時必須滿足 P,然后在 C 和 A 之間進行取舍,這是片面的。實際中網絡出現分區的可能性還是比較小的,尤其是目前網絡環境正在變得越來越好,甚至許多系統都擁有專線支撐,所以在網絡未出現分區時,還是應該兼顧 A 和 C。另外就是對於一致性、可用性,以及分區容錯性三者在度量上也應該有一個評定范圍,最簡單的以可用性來說,當有多少占比請求出現響應超時才可以被認為是不滿足可用性,而不是一出現超時就認為是不可用的。最后我們需要考慮的一點就是分布式系統一般都是一個比較大且復雜的系統,我們應該從更小的粒度上對各個子系統進行評估和設計,而不是簡單的從整體上武斷決策。
讓分布式集群始終對外提供可用的一致性服務一直是富有挑戰和趣味的任務。暫且拋開可用性,拿一致性來說,對於關系型數據庫我們通常利用事務來保證數據的強一致性,但是當我們的數據量越來越大,大到單庫已經無法承擔時,我們不得不采取分庫分表的策略對數據庫實現水平拆分,或者引入 NoSQL 技術,構建分布式數據庫集群以分攤讀寫壓力,從而提升數據庫的存儲和響應能力,但是多個數據庫實例也為我們使用數據庫帶來了許多的限制,比如主鍵的全局唯一、聯表查詢、數據聚合等等,另外一個相當棘手的問題就是數據庫的事務由原先的單庫事務變成了現在的分布式事務。
分布式事務的實現並不是無解的,比如下文要展開的兩階段提交(2PC:Two-Phase Commit)和三階段提交(3PC:Three-Phase Commit)都給我們提供了思路,但是在分布式環境下如何保證數據的強一致性,並對外提供高可用的服務還是相當棘手的,因此很多分布式系統對於數據強一致性都敬而遠之。
兩階段提交協議(2PC:Two-Phase Commit)
兩階段提交協議的目標在於為分布式系統保證數據的一致性,許多分布式系統采用該協議提供對分布式事務的支持。顧名思義,該協議將一個分布式的事務過程拆分成兩個階段: 投票 和 事務提交 。為了讓整個數據庫集群能夠正常的運行,該協議指定了一個 協調者 單點,用於協調整個數據庫集群各節點的運行。為了簡化描述,我們將數據庫集群中的各個節點稱為 參與者 ,三階段提交協議中同樣包含協調者和參與者這兩個角色定義。
第一階段:投票
該階段的主要目的在於打探數據庫集群中的各個參與者是否能夠正常的執行事務,具體步驟如下:
- 協調者向所有的參與者發送事務執行請求,並等待參與者反饋事務執行結果;
- 事務參與者收到請求之后,執行事務但不提交,並記錄事務日志;
- 參與者將自己事務執行情況反饋給協調者,同時阻塞等待協調者的后續指令。
第二階段:事務提交
在經過第一階段協調者的詢盤之后,各個參與者會回復自己事務的執行情況,這時候存在 3 種可能性:
- 所有的參與者都回復能夠正常執行事務。
- 一個或多個參與者回復事務執行失敗。
- 協調者等待超時。
對於第 1 種情況,協調者將向所有的參與者發出提交事務的通知,具體步驟如下:
- 協調者向各個參與者發送 commit 通知,請求提交事務;
- 參與者收到事務提交通知之后執行 commit 操作,然后釋放占有的資源;
- 參與者向協調者返回事務 commit 結果信息。
對於第 2 和第 3 種情況,協調者均認為參與者無法成功執行事務,為了整個集群數據的一致性,所以要向各個參與者發送事務回滾通知,具體步驟如下:
- 協調者向各個參與者發送事務 rollback 通知,請求回滾事務;
- 參與者收到事務回滾通知之后執行 rollback 操作,然后釋放占有的資源;
- 參與者向協調者返回事務 rollback 結果信息。
兩階段提交協議解決的是分布式數據庫數據強一致性問題,實際應用中更多的是用來解決事務操作的原子性,下圖描繪了協調者與參與者的狀態轉換。
站在協調者的角度,在發起投票之后就進入了 WAIT 狀態,等待所有參與者回復各自事務執行狀態,並在收到所有參與者的回復后決策下一步是發送 commit 或 rollback 信息。站在參與者的角度,當回復完協調者的投票請求之后便進入 READY 狀態(能夠正常執行事務),接下去就是等待協調者最終的決策通知,一旦收到通知便可依據決策執行 commit 或 rollback 操作。
兩階段提交協議原理簡單、易於實現,但是缺點也是顯而易見的,包含如下:
- 單點問題
協調者在整個兩階段提交過程中扮演着舉足輕重的作用,一旦協調者所在服務器宕機,就會影響整個數據庫集群的正常運行。比如在第二階段中,如果協調者因為故障不能正常發送事務提交或回滾通知,那么參與者們將一直處於阻塞狀態,整個數據庫集群將無法提供服務。
- 同步阻塞
兩階段提交執行過程中,所有的參與者都需要聽從協調者的統一調度,期間處於阻塞狀態而不能從事其他操作,這樣效率極其低下。
- 數據不一致性
兩階段提交協議雖然是分布式數據強一致性所設計,但仍然存在數據不一致性的可能性。比如在第二階段中,假設協調者發出了事務 commit 通知,但是因為網絡問題該通知僅被一部分參與者所收到並執行了commit 操作,其余的參與者則因為沒有收到通知一直處於阻塞狀態,這時候就產生了數據的不一致性。
針對上述問題可以引入 超時機制 和 互詢機制 在很大程度上予以解決。
對於協調者來說如果在指定時間內沒有收到所有參與者的應答,則可以自動退出 WAIT 狀態,並向所有參與者發送 rollback 通知。對於參與者來說如果位於 READY 狀態,但是在指定時間內沒有收到協調者的第二階段通知,則不能武斷地執行 rollback 操作,因為協調者可能發送的是 commit 通知,這個時候執行 rollback 就會導致數據不一致。
此時,我們可以介入互詢機制,讓參與者 A 去詢問其他參與者 B 的執行情況。如果 B 執行了 rollback 或 commit 操作,則 A 可以大膽的與 B 執行相同的操作;如果 B 此時還沒有到達 READY 狀態,則可以推斷出協調者發出的肯定是 rollback 通知;如果 B 同樣位於 READY 狀態,則 A 可以繼續詢問另外的參與者。只有當所有的參與者都位於 READY 狀態時,此時兩階段提交協議無法處理,將陷入長時間的阻塞狀態。
三階段提交協議(3PC:Three-Phase Commit)
針對兩階段提交存在的問題,三階段提交協議通過引入一個 預詢盤 階段,以及超時策略來減少整個集群的阻塞時間,提升系統性能。三階段提交的三個階段分別為:預詢盤(can_commit)、預提交(pre_commit),以及事務提交(do_commit)。
第一階段:預詢盤
該階段協調者會去詢問各個參與者是否能夠正常執行事務,參與者根據自身情況回復一個預估值,相對於真正的執行事務,這個過程是輕量的,具體步驟如下:
- 協調者向各個參與者發送事務詢問通知,詢問是否可以執行事務操作,並等待回復;
- 各個參與者依據自身狀況回復一個預估值,如果預估自己能夠正常執行事務就返回確定信息,並進入預備狀態,否則返回否定信息。
第二階段:預提交
本階段協調者會根據第一階段的詢盤結果采取相應操作,詢盤結果主要有 3 種:
- 所有的參與者都返回確定信息。
- 一個或多個參與者返回否定信息。
- 協調者等待超時。
針對第 1 種情況,協調者會向所有參與者發送事務執行請求,具體步驟如下:
- 協調者向所有的事務參與者發送事務執行通知;
- 參與者收到通知后執行事務但不提交;
- 參與者將事務執行情況返回給客戶端。
在上述步驟中,如果參與者等待超時,則會中斷事務。 針對第 2 和第 3 種情況,協調者認為事務無法正常執行,於是向各個參與者發出 abort 通知,請求退出預備狀態,具體步驟如下:
- 協調者向所有事務參與者發送 abort 通知;
- 參與者收到通知后中斷事務。
第三階段:事務提交
如果第二階段事務未中斷,那么本階段協調者將會依據事務執行返回的結果來決定提交或回滾事務,分為 3 種情況:
- 所有的參與者都能正常執行事務。
- 一個或多個參與者執行事務失敗。
- 協調者等待超時。
針對第 1 種情況,協調者向各個參與者發起事務提交請求,具體步驟如下:
- 協調者向所有參與者發送事務 commit 通知;
- 所有參與者在收到通知之后執行 commit 操作,並釋放占有的資源;
- 參與者向協調者反饋事務提交結果。
針對第 2 和第 3 種情況,協調者認為事務無法成功執行,於是向各個參與者發送事務回滾請求,具體步驟如下:
- 協調者向所有參與者發送事務 rollback 通知;
- 所有參與者在收到通知之后執行 rollback 操作,並釋放占有的資源;
- 參與者向協調者反饋事務回滾結果。
在本階段如果因為協調者或網絡問題,導致參與者遲遲不能收到來自協調者的 commit 或 rollback 請求,那么參與者將不會如兩階段提交中那樣陷入阻塞,而是等待超時后繼續 commit,相對於兩階段提交雖然降低了同步阻塞,但仍然無法完全避免數據的不一致。
兩階段提交協議中所存在的長時間阻塞狀態發生的幾率還是非常低的,所以雖然三階段提交協議相對於兩階段提交協議對於數據強一致性更有保障,但是因為效率問題,兩階段提交協議在實際系統中反而更加受寵。