一、微服務架構的數據一致性問題數據庫
以電商平臺爲例,當用戶下單並支付後,系統須要修改訂單的狀態而且增長用戶積分。因爲系統採用的是微服務架構,分離出了支付服務、訂單服務和積分服務,每一個服務都有獨立數據庫作數據存儲。當用戶支付成功後,不管是修改訂單狀態失敗仍是增長積分失敗,都會形成數據的不一致。編程
爲了解決例子中的數據一致性問題,一個最直接的辦法就是考慮數據的強一致性。那麼如何保證數據的強一致性呢?咱們從關係型數據庫的 ACID 理論提及。網絡
關係型數據庫具備解決復瑣事務場景的能力,關係型數據庫的事務知足 ACID 的特性。架構
Atomicity:原子性(要麼都作,要麼都不作)併發
Consistency:一致性(數據庫只有一個狀態,不存在未肯定狀態)分佈式
Isolation:隔離性(事務之間互不干擾)函數
Durability: 永久性(事務一旦提交,數據庫記錄永久不變)微服務
具備 ACID 特性的數據庫支持數據的強一致性,保證了數據自己不會出現不一致。高併發
然而微服務架構下,每一個微服務都有本身的數據庫,致使微服務架構的系統不能簡單地知足 ACID,咱們就須要尋找微服務架構下的數據一致性解決方案。性能
微服務架構的系統自己是一種分佈式系統,而本文討論的問題其實也就是分佈式事務之數據一致性的問題,咱們來聊聊分佈式系統的 CAP 理論和 BASE 理論。
CAP 是指在一個分佈式系統下, 包含三個要素:Consistency(一致性)、Availability(可用性)、Partition tolerance(分區容錯性),而且三者不可得兼。
C:Consistency,一致性,全部數據變更都是同步的。
A:Availability,可用性,即在能夠接受的時間範圍內正確地響應用戶請求。
P:Partition tolerance,分區容錯性,即某節點或網絡分區故障時,系統仍可以提供知足一致性和可用性的服務
關係型數據庫 單節點 保證了數據強一致性(C)和可用性(A),可是卻沒法保證分區容錯性(P)。
然而在分佈式系統下,爲了保證模塊的分區容錯性(P),只能在數據強一致性(C)和可用性(A)之間作平衡。具體表現爲在必定時間內,可能模塊之間數據是不一致的,可是經過自動或手動補償後可以達到最終的一致。
BASE 理論主要是解決 CAP 理論中分佈式系統的可用性和一致性不可兼得的問題。BASE 理論包含如下三個要素:
BA:Basically Available,基本可用。
S:Soft State,軟狀態,狀態能夠有一段時間不一樣步。
E:Eventually Consistent,最終一致,最終數據是一致的就能夠了,而不是時時保持強一致。
BASE 模型與 ACID 不一樣,知足 CAP 理論,經過 犧牲強一致性來保證系統可用性。因爲犧牲了強一致性,系統在處理請求的過程當中,數據能夠存在短時的不一致。
系統在處理業務時,記錄每一步的臨時狀態。當出現異常時,根據狀態判斷是否繼續處理請求或者退回原始狀態,從而達到數據的最終一致。
例如,在上面的案例中,支付成功,訂單也成功,但增長積分失敗,此時,不該回滾支付和訂單,而應經過一些 補償方法 來讓積分得以正確地增長。後面會講到具體的實現方法。
在分享咱們的分佈式事務實踐方案以前,先看看早期解決分佈式事務問題的二階段提交協議。
二、二階段提交協議
X/Open DTP(Distributed Transaction Process)是一個分佈式事務模型,此模型主要使用二階段提交(2PC,Two-Phase-Commit)來保證分佈式事務的完整性。在這個模型裏面,有三個角色:
AP:Application,應用程序,業務層。
RM:Resource Manager,資源管理器,關係型數據庫或支持 XA 接口(XA 規範是 X/Open 組織定義的分佈式事務規範)的組件。
TM: Transaction Manager ,事務管理器,負責各個 RM 的提交和回滾。
當應用程序(AP)調用了事務管理器(TM)的提交方法時,事務的提交分爲兩個階段實行。
2.一、第一階段(準備階段)
TM 通知全部參與事務的各個 RM,給每一個 RM 發送 prepare 消息。
RM 接收到消息後進入準備階段後,要麼直接返回失敗,要麼建立並執行本地事務,寫本地事務日誌(redo 和 undo 日誌),可是 不提交(此處只保留最後一步耗時最少的提交操做給第二階段執行)。
2.二、第二階段(提交 / 回滾階段)
TM 收到 RM 準備階段的失敗消息或者獲取 RM 返回消息超時,則直接給 RM 發送回滾(rollback)消息,不然發送提交(commit)消息。
RM 根據 TM 的指令執行提交或者回滾,執行完成後釋放全部事務處理過程當中使用的鎖(最後階段釋放鎖)。
2.三、二階段提交的利弊
優勢
2PC 提供了一套完整的分佈式事務的解決方案,遵循事務嚴格的 ACID 特性。
缺點
TM 經過 XA 接口與各個 RM 之間進行數據交互,從第一階段的準備階段,業務所涉及的數據就被鎖定,而且鎖定跨越整個提交流程。在高併發和涉及業務模塊較多的狀況下對數據庫的性能影響較大。
二階段是 反可伸縮模式 的,業務規模越大,涉及模塊越多,侷限性越大,系統可伸縮性越差。
在技術棧比較雜的分佈式應用中,存儲組件有不少 不支持 XA 協議。
二階段的諸多弊端,致使分佈式系統下沒法直接使用此方案來解決數據一致性問題,但它提供瞭解決分佈式系統下數據一致性問題的思路。。
下面就經過案例來分享咱們是如何保證微服務架構的數據一致性的。
可靠事件模式屬於事件驅動架構,當某件重要事情發生時,例如更新一個業務實體,微服務會向消息代理髮佈一個事件。消息代理會向訂閱事件的微服務推送事件,當訂閱這些事件的微服務接收此事件時,就能夠完成本身的業務,也可能會引起更多的事件發佈。
這個過程可能致使出現不一致的地方在於:某個微服務在更新了業務實體後發佈事件卻失敗;雖然微服務發佈事件成功,可是消息代理未能正確推送事件到訂閱的微服務;接受事件的微服務重複消費了事件。
可靠事件模式在於保證可靠事件投遞和避免重複消費,可靠事件投遞定義爲
(a)每一個服務原子性的業務操做和發佈事件
(b)消息代理確保事件傳遞至少一次。
避免重複消費要求服務實現冪等性,如支付服務不能由於重複收到事件而屢次支付。
爲了描述方便,這裏先定義兩個概念:
業務異常:業務邏輯產生錯誤的狀況,好比帳戶餘額不足、商品庫存不足等。
技術異常:非業務邏輯產生的異常,如網絡鏈接異常、網絡超時等。
補償模式使用一個額外的協調服務來協調各個須要保證一致性的微服務,協調服務按順序調用各個微服務,若是某個微服務調用異常(包括業務異常和技術異常)就取消以前全部已經調用成功的微服務。
補償模式建議僅用於不能避免出現業務異常的狀況,若是有可能應該優化業務模式,以免要求補償事務。如帳戶餘額不足的業務異常可經過預先凍結金額的方式避免,商品庫存不足可要求商家準備額外的庫存等。
咱們經過一個實例來講明補償模式,一家旅行公司提供預訂行程的業務,能夠經過公司的網站提早預訂飛機票、火車票、酒店等。
假設一位客戶規劃的行程是,(1)上海-北京6月19日9點的某某航班,(2)某某酒店住宿3晚,(3)北京-上海6月22日17點火車。在客戶提交行程後,旅行公司的預訂行程業務按順序串行的調用航班預訂服務、酒店預訂服務、火車預訂服務。最後的火車預訂服務成功後整個預訂業務纔算完成。
若是火車票預訂服務沒有調用成功,那麼以前預訂的航班、酒店都得取消。取消以前預訂的酒店、航班即爲補償過程。
須要注意的是酒店的取消預訂、航班的取消預訂一樣不能保證必定成功,因此補償過程每每也一樣須要實現最終一致性,須要保證取消服務至少被調用一次和取消服務必須實現冪等性。
咱們應該儘量經過設計避免採用補償方式,好比上面的例子中,在預訂火車票失敗的時候能夠提示客戶更改其餘的時間。
一個完整的TCC業務由一個主業務服務和若干個從業務服務組成,主業務服務發起並完成整個業務活動,TCC模式要求從服務提供三個接口:Try、Confirm、Cancel。
第一階段:主業務服務分別調用全部從業務的try操做,並在活動管理器中登記全部從業務服務。當全部從業務服務的try操做都調用成功或者某個從業務服務的try操做失敗,進入第二階段。
第二階段:活動管理器根據第一階段的執行結果來執行confirm或cancel操做。若是第一階段全部try操做都成功,則活動管理器調用全部從業務活動的confirm操做。不然調用全部從業務服務的cancel操做。
須要注意的是第二階段confirm或cancel操做自己也是知足最終一致性的過程,在調用confirm或cancel的時候也可能由於某種緣由(好比網絡)致使調用失敗,因此須要活動管理支持重試的能力,同時這也就要求confirm和cancel操做具備冪等性。
在編程中一個冪等操做的特色是其任意屢次執行所產生的影響均與一次執行的影響相同。
冪等函數,或冪等方法,是指可使用相同參數重複執行,並能得到相同結果的函數。這些函數不會影響系統狀態,也不用擔憂重複執行會對系統形成改變。
在事務提交失敗會重複提交達到最大重複次數時返回失敗的 設計中 保持冪等性是尤其重要的