近來常常用到分佈式事務,這裏總結一下,咱們目前的使用場景基本都是採用事務消息方式。那麼說到分佈式不得不談的CAPnode
一個分佈式系統最多隻能同時知足一致性(Consistency)、可用性(Availability)和分區容錯性(Partition tolerance)這三項中的兩項。算法
一致性指「all nodes see the same data at the same time」,即更新操做成功並返回客戶端完成後,全部節點在同一時間的數據徹底一致。sql
對於一致性,能夠分爲從客戶端和服務端兩個不一樣的視角。從客戶端來看,一致性主要指的是多併發訪問時更新過的數據如何獲取的問題。從服務端來看,則是更新如何複製分佈到整個系統,以保證數據最終一致。一致性是由於有併發讀寫纔有的問題,所以在理解一致性的問題時,必定要注意結合考慮併發讀寫的場景。數據庫
從客戶端角度,多進程併發訪問時,更新過的數據在不一樣進程如何獲取的不一樣策略,決定了不一樣的一致性。對於關係型數據庫,要求更新過的數據能被後續的訪問都能看到,這是強一致性。若是能容忍後續的部分或者所有訪問不到,則是弱一致性。若是通過一段時間後要求能訪問到更新後的數據,則是最終一致性。編程
可用性指「Reads and writes always succeed」,即服務一直可用,並且是正常響應時間。網絡
對於一個可用性的分佈式系統,每個非故障的節點必須對每個請求做出響應。也就是,該系統使用的任何算法必須最終終止。當同時要求分區容忍性時,這是一個很強的定義:即便是嚴重的網絡錯誤,每一個請求必須終止。併發
好的可用性主要是指系統可以很好的爲用戶服務,不出現用戶操做失敗或者訪問超時等用戶體驗很差的狀況。可用性一般狀況下可用性和分佈式數據冗餘,負載均衡等有着很大的關聯。負載均衡
分區容錯性指「the system continues to operate despite arbitrary message loss or failure of part of the system」,即分佈式系統在遇到某節點或網絡分區故障的時候,仍然可以對外提供知足一致性和可用性的服務。分佈式
分區容錯性和擴展性緊密相關。在分佈式應用中,可能由於一些分佈式的緣由致使系統沒法正常運轉。好的分區容錯性要求可以使應用雖然是一個分佈式系統,而看上去卻好像是在一個能夠運轉正常的總體。好比如今的分佈式系統中有某一個或者幾個機器宕掉了,其餘剩下的機器還可以正常運轉知足系統需求,或者是機器之間有網絡異常,將分佈式系統分隔未獨立的幾個部分,各個部分還能維持分佈式系統的運做,這樣就具備好的分區容錯性。微服務
如上圖,是咱們證實CAP的基本場景,網絡中有兩個節點N1和N2,能夠簡單的理解N1和N2分別是兩臺計算機,他們之間網絡能夠連通,N1中有一個應用程序A,和一個數據庫V,N2也有一個應用程序B2和一個數據庫V。如今,A和B是分佈式系統的兩個部分,V是分佈式系統的數據存儲的兩個子數據庫。
在知足一致性的時候,N1和N2中的數據是同樣的,V0=V0。在知足可用性的時候,用戶不論是請求N1或者N2,都會獲得當即響應。在知足分區容錯性的狀況下,N1和N2有任何一方宕機,或者網絡不通的時候,都不會影響N1和N2彼此之間的正常運做。
如上圖,是分佈式系統正常運轉的流程,用戶向N1機器請求數據更新,程序A更新數據庫Vo爲V1,分佈式系統將數據進行同步操做M,將V1同步的N2中V0,使得N2中的數據V0也更新爲V1,N2中的數據再響應N2的請求。
這裏,能夠定義N1和N2的數據庫V之間的數據是否同樣爲一致性;外部對N1和N2的請求響應爲可用行;N1和N2之間的網絡環境爲分區容錯性。這是正常運做的場景,也是理想的場景,然而現實是殘酷的,當錯誤發生的時候,一致性和可用性還有分區容錯性,是否能同時知足,仍是說要進行取捨呢?
做爲一個分佈式系統,它和單機系統的最大區別,就在於網絡,如今假設一種極端狀況,N1和N2之間的網絡斷開了,咱們要支持這種網絡異常,至關於要知足分區容錯性,能不能同時知足一致性和響應性呢?仍是說要對他們進行取捨。
假設在N1和N2之間網絡斷開的時候,有用戶向N1發送數據更新請求,那N1中的數據V0將被更新爲V1,因爲網絡是斷開的,因此分佈式系統同步操做M,因此N2中的數據依舊是V0;這個時候,有用戶向N2發送數據讀取請求,因爲數據尚未進行同步,應用程序沒辦法當即給用戶返回最新的數據V1,怎麼辦呢?有二種選擇,第一,犧牲數據一致性,響應舊的數據V0給用戶;第二,犧牲可用性,阻塞等待,直到網絡鏈接恢復,數據更新操做M完成以後,再給用戶響應最新的數據V1。
這個過程,證實了要知足分區容錯性的分佈式系統,只能在一致性和可用性二者中,選擇其中一個。
經過CAP理論,咱們知道沒法同時知足一致性、可用性和分區容錯性這三個特性,那要捨棄哪一個呢?
CA without P:若是不要求P(不容許分區),則C(強一致性)和A(可用性)是能夠保證的。但其實分區不是你想不想的問題,而是始終會存在,所以CA的系統更多的是容許分區後各子系統依然保持CA。
CP without A:若是不要求A(可用),至關於每一個請求都須要在Server之間強一致,而P(分區)會致使同步時間無限延長,如此CP也是能夠保證的。不少傳統的數據庫分佈式事務都屬於這種模式。
AP wihtout C:要高可用並容許分區,則需放棄一致性。一旦分區發生,節點之間可能會失去聯繫,爲了高可用,每一個節點只能用本地數據提供服務,而這樣會致使全局數據的不一致性。如今衆多的NoSQL都屬於此類。
對於多數大型互聯網應用的場景,主機衆多、部署分散,並且如今的集羣規模愈來愈大,因此節點故障、網絡故障是常態,並且要保證服務可用性達到N個9,即保證P和A,捨棄C(退而求其次保證最終一致性)。雖然某些地方會影響客戶體驗,但沒達到形成用戶流程的嚴重程度。
對於涉及到錢財這樣不能有一絲讓步的場景,C必須保證。網絡發生故障寧肯中止服務,這是保證CA,捨棄P。貌似這幾年國內銀行業發生了不下10起事故,但影響面不大,報到也很少,廣大羣衆知道的少。還有一種是保證CP,捨棄A。例如網絡故障事只讀不寫。
孰優孰略,沒有定論,只能根據場景定奪,適合的纔是最好的。
微服務的盛行,處處都是分佈式事務。好比咱們的訂單系統和庫存系統怎麼保證扣庫存
和生成訂單
的原子性。
這裏分紅兩大類來介紹
兩階段提交是基於XA(分佈式事務協議),由數據庫實現XA接口,只要數據庫實現了協議,使用比較簡單,不過併發性能不理想,在此不詳述;
分爲三個階段TRYING-CONFIRMING-CANCELING。每一個階段作不一樣的處理。
TRYING階段主要是對業務系統進行檢測及資源預留
CONFIRMING階段是作業務提交,經過TRYING階段執行成功後,再執行該階段。默認若是TRYING階段執行成功,CONFIRMING就必定能成功。
CANCELING階段是回對業務作回滾,在TRYING階段中,若是存在分支事務TRYING失敗,則須要調用CANCELING將已預留的資源進行釋放。
拿訂單庫存來舉例:Try(扣庫存),Confirm(更新訂單),若是更新訂單失敗,就進入Cancel階段回滾(恢復庫存),回滾階段由業務編碼實現(再來一個sql把庫存加回去),不一樣場景不一樣考慮,很差複用;
發送方首先把消息發送到MQ Server,狀態是」未提交「,主要是起到數據落地的做用,爲後續可能出現的回查過程存根。至因而否真的要投遞這個消息取決於隨後的事務提交狀態。
使用TransactionSynchronizationManager.registerSynchronization(TransactionSynchronization synchronization)註冊事務同步器,擴展點由Spring提供,通常爲一個通用實現。核心邏輯以下(僞代碼,並不是實際實現,只表達核心思路):
public void afterCompletion(int status) { boolean committed = false; if (status == 0) { committed = true; } else { if (status != 1) { //事務狀態unknown,等回查 return } committed = false; } //通知MQ Server本地事務結果 send(committed) }
如上,同步器會將本地事務的執行結果發送給MQ Server,異常狀況交給回查方法來解決。