分佈式事務 CAP 理解論證 解決方案

前言
在大型系統架構演變中,當前下,分佈式是一個必然的選擇,分佈式事務是繞不開的一個點.spring

目錄
概述
論證
解決方案
3.1 維護本地消息表
3.2 使用rocketmq事務消息
3.3 兩階段提交協議(2PC)
3.4 TCC事務補償機制
正文
1.概述
在單體架構中,咱們的事務能夠經過數據庫的ACID來操做,不會出現什麼問題.數據庫

1.1 問題描述:
但隨着規模擴大,咱們的邏輯服務進行拆分A,B,C…模塊,部署在多臺服務器,數據庫通常也是多臺,進行了分庫分表等操做,這些A,B,C…模塊間經過網絡通訊完成協做,那此刻就產生了單體應用觸發不了的問題:瀏覽器

一致性問題: 既然是多個數據庫,那麼經過網絡操做,客觀上就會存在延時(短距離很小,當跨國時就很慢了)甚至不可達,
可用性問題: 用戶訪問某個頁面,應該給提供可預期的結果,而不是瀏覽器報錯,404,500,頁面丟失…等等,能夠經過一些策略完成
網絡分區:在分佈式場景中,不可避免的會出現多個模塊系統協同工做,根據墨菲定律,就必定有概率發生網絡中斷,    延時,不可達等客觀存在問題
產生的問題不止這三種,但其餘的都是可處理的問題,這三種是大頭
注意誤區: 由於先有分區的存在,才致使了一致性和可用性問題緩存

1.2 CAP 理論:
而對於分佈式中的問題的解決方案,CAP原則出現,描述以下:springboot

一致性(Consistency):
像A節點寫入一條信息以後,同一時刻,在其餘節點均可以讀到這條信息服務器

可用性(Availability):
多布一些節點A,B,C…,任什麼時候刻,用戶訪問,都應該以可預期的結果返回,而不是瀏覽器報錯,404,500,頁面丟失…等用戶體驗很差的狀況發生網絡

分區容忍性(PartitionTolerance):
當各系統模塊間通訊出現問題時,設計一個策略,使系統仍可對外提供知足一致性或可用性架構

剛接觸cap時,有些不理解分區容忍性,咱們本身倒推一下:併發

爲了保證一致性,咱們須要各個節點同步消息
爲了保證可用性咱們能夠多部署節點,部分節點掛了仍可對外提供服務
爲了保證分區容忍性:此刻卡殼了,怎麼作?沒了一種具體的方式,然而他仍是客觀存在的
後來發現:進入了思惟盲點:只要在分佈式場景中,分區必然存在,那麼若是不處理分區發生時的狀況,節點沒法通信時會發生什麼?–此刻若是仍對外提供服務,那麼致使沒法同步消息,即保證不了強一致性;若是要保證強一致性,那麼就須要節點阻塞,一直等待通信恢復,即保證不了可用性.
因此分區容忍性就是:當發生分區問題時,咱們使用策略,在一致性和可用性兩者間選擇
注意: 沒法通訊包括網絡問題,或者節點機器宕機
誤區: CAP理論中說三者不可兼得,但實際狀況是,在分佈式場景中分區必定存在,即必須有分區容忍性對應的策略,以後才能在一致性和可用性間兩者之間選擇.因此對主流架構來講不是三選二,而是二選一分佈式

1.3 三種組合:
分佈式系統中,爲了提升強一致性,應該使用更少的節點,這樣更容易同步消息,而爲了提升可用性,不得不增長節點,保證高可用,矛盾,因此須要取捨:

CA:
保證可用性和一致性,放棄分區:除非不是分佈式架構,或者應用在一個永不會通訊故障的網絡中(理想),只有個別場景符合,當前的互聯網架構顯然不符合使用
CP:
保證一致性和分區容忍性,放棄可用性:當節點間不可通訊時,進行阻塞,直到通訊恢復,期間沒法再對外提供服務,用戶體驗很差,如A轉帳給B,只有A扣款成功並B收款成功,整個事務纔算完成,顯然耗費資源
AP:
保證可用性和分區容忍性,放棄強一致性(使用最終一致性):給出一個用戶能夠忍受的時間,時間內達成數據的最終一致性,好比跨行轉帳,並非馬上到帳,多是明天,或者2小時內到帳

2 論證
操做: 在北京和上海各有一個節點Node1和Node2,咱們對Node1插入一條數據
看到這裏,就假定默認選擇了分區容忍性,在此基礎上抉擇A和C:

2.1在網絡穩定時:
向Node1插入一條數據,數據能夠同步到Node2,在北京和上海訪問,沒什麼區別


2.2在網絡中斷時:
向Node1插入一條數據,Node1在向Node2同步時,網絡中斷,假如北京和上海的用戶是訪問的兩個節點,此刻你剛插入 的數據在上海是訪問不到的,此刻就要進行抉擇:
1.保留強一致性:節點1同步到節點2以前,就不顯示插入成功,一直阻塞,直到同步成功,
2.保留可用性:向節點1插入一條消息後,用一條消息通知節點2(這個消息要確保成功),此時給用戶返回插入成功,等網絡恢復後,執行這條消息便可;除非特大天然災害,通常的網絡問題均可以短期恢復.


可根據各自的業務場景,選擇對應的策略
注意: 隨着規模的繼續擴大,節點更多,咱們維持一致性的成本更高

3 解決方案
3.1 維護本地消息表
分佈式事務就是跨多個系統的事務,能夠拆分爲多個子系統的本地事務;
分佈式事務=A系統本地事務 + B系統本地事務 + 消息通知;
準備: A系統維護一張消息表log1,狀態爲未執行,B系統維護2張表,未完成表log2,已完成表log3,消息中間件用兩個topic,topic1是A系統通知B要執行任務了,topic2是B系統通知A已經完成任務了,

1.用戶在A系統裏領取優惠券,並往log1插入一條記錄
2.由定時任務輪詢log1,發消息給B系統
3.B系統收到消息後,先檢查是否在log3中執行過這條消息,沒有的話插入log2表,並進行發短信,發送成功後刪除log2的記錄,插入log3
4.B系統發消息給A系統
5.A系統根據id刪除這個消息

咱們假設網絡中斷:
1.在1處中斷:此時咱們插入優惠券和log1用的本地事務,即便發消息失敗,有定時任務輪詢,會再次發送
2.在2處中斷:當B系統發短信後,通知A系統失敗,由於A系統有定時任務輪詢,會重複再發一次,因此B系統會先檢查log3,若是已經執行過了,就不發短信了,再次給A系統發送執行完成的消息,

實現最終事務一致要求:
預留資源成功理論上要求正式執行成功,若是執行失敗會進行重試,要求業務執行方法實現冪等,每次執行的結果不變;
**優勢:**開發簡單,mq性能較高
**缺點:**業務耦合,由於頻繁輪詢數據庫,增大了數據庫負載,此時數據庫的性能瓶頸尤其明顯,不適合大高併發場景,中等的規模仍是能夠知足的

3.2 使用rocketmq事務消息
阿里巴巴的rocketmq支持事務消息,實現機制,能夠參考另外一篇博客:https://blog.csdn.net/weixin_40533111/article/details/84451219

3.3 兩階段提交協議(2PC)
爲解決分佈式系統的數據一致性問題出現了兩階段提交協議(2 Phase Commitment Protocol),兩階段提交由
協調者和參與者組成,共通過兩個階段和三個操做,部分關係數據庫如Oracle、MySQL支持兩階段提交協議.
流程圖:

1)第一階段:準備階段(prepare)
協調者通知參與者準備提交訂單,參與者開始投票。
協調者完成準備工做向協調者回應Yes。
2)第二階段:提交(commit)/回滾(rollback)階段
協調者根據參與者的投票結果發起最終的提交指令。
若是有參與者沒有準備好則發起回滾指令。
一個下單減庫存的例子:


一、應用程序鏈接兩個數據源。
二、應用程序經過事務協調器向兩個庫發起prepare,兩個數據庫收到消息分別執行本地事務(記錄日誌),但不提
交,若是執行成功則回覆yes,不然回覆no。
三、事務協調器收到回覆,只要有一方回覆no則分別向參與者發起回滾事務,參與者開始回滾事務。
四、事務協調器收到回覆,所有回覆yes,此時向參與者發起提交事務。若是參與者有一方提交事務失敗則由事務協
調器發起回滾事務。
2PC的優勢:實現強一致性,部分關係數據庫支持(Oracle、MySQL等)。
缺點:整個事務的執行須要由協調者在多個節點之間去協調,增長了事務的執行時間,性能低下。
解決方案有:springboot+Atomikos or Bitronix
3PC主要是解決協調者與參與者通訊阻塞問題而產生的,它比2PC傳遞的消息還要多,性能不高。詳細參考3PC:

3.4 TCC事務補償機制
TCC事務補償是基於2PC實現的業務層事務控制方案,它是Try、Confirm和Cancel三個單詞的首字母,含義以下:

Try 檢查及預留業務資源
完成提交事務前的檢查,並預留好資源。
Confirm 肯定執行業務操做
對try階段預留的資源正式執行。
Cancel 取消執行業務操做
對try階段預留的資源釋放。
示例:

Try 下單業務由訂單服務和庫存服務協同完成,在try階段訂單服務和庫存服務完成檢查和預留資源。 訂單服務檢查當前是否知足提交訂單的條件(好比:當前存在未完成訂單的不容許提交新訂單)。 庫存服務檢查當前是否有充足的庫存,並鎖定資源。 Confirm 訂單服務和庫存服務成功完成Try後開始正式執行資源操做。 訂單服務向訂單寫一條訂單信息。 庫存服務減去庫存。 Cancel 若是訂單服務和庫存服務有一方出現失敗則所有取消操做。 訂單服務須要刪除新增的訂單信息。 庫存服務將減去的庫存再還原。 優勢:最終保證數據的一致性,在業務層實現事務控制,靈活性好。XA兩階段提交資源層面的,而TCC實際上把資源層面二階段提交上提到了業務層面來實現。有效了的避免了XA兩階段提交佔用資源鎖時間過長致使的性能地下問題。 缺點:開發成本高,每一個事務操做每一個參與者都須要實現try/confirm/cancel三個接口。 注意:TCC的try/confirm/cancel接口都要實現冪等性,在爲在try、confirm、cancel失敗後要不斷重試;它讓多個系統保證了原子性操做,所以成本仍是比較高的。 冪等性: 冪等性是指同一個操做不管請求多少次,其結果都相同。 冪等操做實現方式有: 一、操做以前在業務方法進行判斷若是執行過了就再也不執行。 二、緩存全部請求和處理的結果,已經處理的請求則直接返回結果 三、在數據庫表中加一個狀態字段(未處理,已處理),數據操做時判斷未處理時再處理。

相關文章
相關標籤/搜索