事務就是對數據庫的一次操做,要麼所有成功,要麼所有失敗。事務是最小的邏輯執行單元,也是數據庫併發控制的基本單位。其做用就是確保數據的準確性。java
原子性是指事務包含的全部操做要麼所有成功,要麼所有失敗回滾。保證事務內的操做是不可分割的。node
一致性是指事務執行的結果必須使數據庫從一種一致性狀態變到另外一種一致性狀態。好比轉帳,假設用戶A和用戶B二者的錢加起來一共是2000,那麼無論A和B之間如何轉帳,轉幾回帳,事務結束後兩個用戶的錢相加起來應該還得是2000,不可能出現其餘狀況。mysql
隔離性是指併發執行的事務之間不能相互影響。好比寫一條update語句和delete語句,並且同時操做一條記錄,若是不加控制那就會出現各類問題。git
持久性是指一個事務一旦被提交了,那麼對數據庫中的數據的改變就是永久性的,即使是在數據庫系統遇到故障的狀況下也不會丟失提交事務的操做。好比執行insert語句後,就必需要有數據寫入磁盤。程序員
總之,這四個特性是事務管理的基石,其中,原子性是基礎,隔離性是手段,持久性是目的,真正的老大就是一致性。其中最難的是隔離性,由於事務是原子操做,但事務與事務之間能夠併發執行,所以要達到一致性就要求事務與事務之間進行隔離,那麼事務若是不隔離直接併發執行會形成什麼後果呢?github
髒讀是指在一個事務處理過程裏讀取了另外一個未提交的事務中的數據。spring
時間 | 事務 A(存款) | 事務 B(取款) |
T1 | 開始事務 | |
T2 | 開始事務 | |
T3 | 查詢餘額(500 元) | |
T4 | 取出 500 元(餘額 0 元) | |
T5 | 查詢餘額(0 元) | |
T6 | 撤銷事務(餘額恢復爲 500 元) | |
T7 | 存入 100 元(餘額 100 元) | |
T8 | 提交事務 |
餘額應該爲 600 元纔對!請看 T5 時間點,事務 A 此時查詢餘額爲 0 元,這個數據就是髒數據,它是事務 B 形成的,明顯事務沒有進行隔離,滲過來了,亂套了。sql
不可重複讀是指在對於數據庫中的某個數據,一個事務範圍內屢次查詢卻返回了不一樣的數據結果,這是因爲在查詢間隔,被另外一個事務修改並提交了。 數據庫
時間 | 事務 A(存款) | 事務 B(取款) |
T1 | 開始事務 | |
T2 | 開始事務 | |
T3 | 查詢餘額(500 元) | |
T4 | 查詢餘額(500 元) | |
T5 | 取出 500 元(餘額 0 元) | |
T6 | 提交事務 | |
T7 | 查詢餘額(0 元) |
事務 A 其實除了查詢了兩次之外,其餘什麼事情都沒有作,結果錢就從 1000 變成 0 了,這就是重複讀了。可想而知,這是別人乾的,不是我乾的。其實這樣也是合理的,畢竟事務 B 提交了事務,數據庫將結果進行了持久化,因此事務 A 再次讀取天然就發生了變化。這種現象也正常,但在某些場景下確實不容許的(好比電商中扣減庫存)。express
幻讀是事務非獨立執行時發生的一種現象,即在一個事務讀的過程當中,另一個事務可能插入了新數據記錄,影響了該事務讀的結果。
時間 | 事務 A(統計總存款) | 事務 B(存款) |
---|---|---|
T1 | 開始事務 | |
T2 | 開始事務 | |
T3 | 統計總存款(1000 元) | |
T4 | 存入 100 元 | |
T5 | 提交事務 | |
T6 | 統計總存款(1100 元) |
概括一下,以上提到了事務併發所引發的跟讀取數據有關的問題,各用一句話來描述一下:
第一條是堅定抵制的,後兩條在大多數狀況下可不做考慮。正由於事務併發時會出現問題,所以數據庫專家們就提出瞭解決方案即事務的隔離級別。
不一樣數據庫的事務隔離級別不盡相同。MySQL數據庫支持下面的四種隔離級別,而且5.5之後默認爲 Repeatable read 級別,以前是 Read committed ;而在Oracle數據庫中,只支持Serializable 級別和 Read committed 這兩種級別,而且默認爲 Read committed 級別。MySQL數據庫爲咱們提供了四種隔離級別,分別爲:
事務隔離級別 | 髒讀 | 不可重複讀 | 幻讀 |
---|---|---|---|
READ_UNCOMMITTED | 容許 | 容許 | 容許 |
READ_COMMITTED | 禁止 | 容許 | 容許 |
REPEATABLE_READ | 禁止 | 禁止 | 容許 |
SERIALIZABLE | 禁止 | 禁止 | 禁止 |
# 查詢隔離級別 select @@tx_isolation; # 設置隔離級別 set [glogal | session] transaction isolation level 隔離級別名稱;
若是咱們的事務處理都在同一個數據庫中,那簡稱爲本地事務。在Java中,可使用JDBC對設置數據庫事務的隔離級別。設置數據庫的隔離級別必定要是在開啓事務以前。使用JDBC對數據庫的事務設置隔離級別時,咱們應該在調用Connection對象的setAutoCommit(false)方法以前調用Connection對象的setTransactionIsolation(level)去設置當前Connection的隔離級別以下所示:
至於參數level,可使用Connection接口的字段,如如下代碼所示:
這種設置方式只對當前connection有效。JDBC 只是鏈接 Java 程序與數據庫的橋樑而已,那麼數據庫又是怎樣隔離事務的呢?其實它就是「鎖」這個東西。當插入數據時,就鎖定表,這叫「鎖表」;當更新數據時,就鎖定行,這叫「鎖行」。
Spring框架提供了一套事務傳播行爲(Transaction Propagation Behavior)機制來解決本地事務管理,其實它是對 JDBC 的一個補充或擴展。Spring一共有七種事務傳播行爲:
事務傳播行爲類型 | 說明 |
---|---|
PROPAGATION_REQUIRED | 若是當前沒有事務,就新建一個事務,若是已經存在一個事務中,加入到這個事務中。這是最多見的選擇。默認 |
PROPAGATION_SUPPORTS | 支持當前事務,若是當前沒有事務,就以非事務方式執行。 |
PROPAGATION_MANDATORY | 使用當前的事務,若是當前沒有事務,就拋出異常。 |
PROPAGATION_REQUIRES_NEW | 新建事務,若是當前存在事務,把當前事務掛起。 |
PROPAGATION_NOT_SUPPORTED | 以非事務方式執行操做,若是當前存在事務,就把當前事務掛起。 |
PROPAGATION_NEVER | 以非事務方式執行,若是當前存在事務,則拋出異常。 |
PROPAGATION_NESTED | 若是當前存在事務,則在嵌套事務內執行。若是當前沒有事務,則執行與PROPAGATION_REQUIRED相似的操做。 |
配置demo以下:
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"/> </bean> <tx:advice id="txAdvice" transaction-manager="transactionManager"> <tx:attributes> <tx:method name="query*" propagation="REQUIRED" read-only="true"/> <tx:method name="save*" propagation="REQUIRED"/> <tx:method name="insert*" propagation="REQUIRED"/> <tx:method name="batchSave*" propagation="REQUIRED"/> <tx:method name="update*" propagation="REQUIRED"/> <tx:method name="cancelOrder" propagation="REQUIRED"/> <tx:method name="*" propagation="SUPPORTS" read-only="true" /> </tx:attributes> </tx:advice> <aop:config> <aop:pointcut id="servicePoint" expression="execution(* com.lzj.*.service..*.*(..)) "/> <aop:advisor advice-ref="txAdvice" pointcut-ref="servicePoint"/> </aop:config>
或者使用@Transactional註解,裏面也能夠配置隔離級別:
@Target({ElementType.TYPE, ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Inherited @Documented public @interface Transactional { // 事務管理器別名 @AliasFor("transactionManager") String value() default ""; // 事務管理器 @AliasFor("value") String transactionManager() default ""; // 事務傳播機制 Propagation propagation() default Propagation.REQUIRED; // 隔離級別 Isolation isolation() default Isolation.DEFAULT; // 事務超時時間 int timeout() default TransactionDefinition.TIMEOUT_DEFAULT; // 是否爲只讀事務 boolean readOnly() default false; // 事務回滾異常類 Class<? extends Throwable>[] rollbackFor() default {}; // 事務回滾異常類名 String[] rollbackForClassName() default {}; // 事務不回滾異常類 Class<? extends Throwable>[] noRollbackFor() default {}; // 事務不回滾異常類名 String[] noRollbackForClassName() default {}; }
全新Java資料加入羣:10803-55292,輸入暗號13,便可領取
Spring 給咱們帶來了事務傳播行爲,這確實是一個很是強大而又實用的功能。除此之外,也提供了一些小的附加功能,好比:
有的狀況下,咱們的事務並不必定在同一個數據庫或者同一個線程中,好比:跨庫事務、分表事務、分佈式應用下不一樣應用之間的事務,那麼這些都通叫分佈式事務。
全新Java資料加入羣:10803-55292,輸入暗號13,便可領取
聊分佈式事務之間咱們先了解下分佈式理論。
CAP定理是由加州大學伯克利分校Eric Brewer教授提出來的,他指出WEB服務沒法同時知足一下3個屬性:
一致性指「all nodes see the same data at the same time
」,即更新操做成功並返回客戶端完成後,全部節點在同一時間的數據徹底一致,因此,一致性,說的就是數據一致性。分佈式的一致性
對於一致性,能夠分爲從客戶端和服務端兩個不一樣的視角。從客戶端來看,一致性主要指的是多併發訪問時更新過的數據如何獲取的問題。從服務端來看,則是更新如何複製分佈到整個系統,以保證數據最終一致。
一致性是由於有併發讀寫纔有的問題,所以在理解一致性的問題時,必定要注意結合考慮併發讀寫的場景。
從客戶端角度,多進程併發訪問時,更新過的數據在不一樣進程如何獲取的不一樣策略,決定了不一樣的一致性。
三種一致性策略
對於關係型數據庫,要求更新過的數據能被後續的訪問都能看到,這是強一致性。
若是能容忍後續的部分或者所有訪問不到,則是弱一致性。
若是通過一段時間後要求能訪問到更新後的數據,則是最終一致性。
CAP中說,不可能同時知足的這個一致性指的是強一致性。
可用性指「Reads and writes always succeed
」,即服務一直可用,並且是正常響應時間。
對於一個可用性的分佈式系統,每個非故障的節點必須對每個請求做出響應。因此,通常咱們在衡量一個系統的可用性的時候,都是經過停機時間來計算的。
可用性分類 | 可用水平(%) | 年可容忍停機時間 |
---|---|---|
容錯可用性 | 99.9999 | <1 min |
極高可用性 | 99.999 | <5 min |
具備故障自動恢復能力的可用性 | 99.99 | <53 min |
高可用性 | 99.9 | <8.8h |
商品可用性 | 99 | <43.8 min |
一般咱們描述一個系統的可用性時,咱們說淘寶的系統可用性能夠達到5個9,意思就是說他的可用水平是99.999%,即整年停機時間不超過 (1-0.99999)*365*24*60 = 5.256 min
,這是一個極高的要求。
好的可用性主要是指系統可以很好的爲用戶服務,不出現用戶操做失敗或者訪問超時等用戶體驗很差的狀況。一個分佈式系統,上下游設計不少系統如負載均衡、WEB服務器、應用代碼、數據庫服務器等,任何一個節點的不穩定均可以影響可用性。
分區容錯性指「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理論及前面的證實,咱們知道沒法同時知足一致性、可用性和分區容錯性這三個特性,那要捨棄哪一個呢?
咱們分三種狀況來闡述一下。
這種狀況在分佈式系統中幾乎是不存在的。首先在分佈式環境下,網絡分區是一個天然的事實。由於分區是必然的,因此若是捨棄P,意味着要捨棄分佈式系統。那也就沒有必要再討論CAP理論了。這也是爲何在前面的CAP證實中,咱們以系統知足P爲前提論述了沒法同時知足C和A。
好比咱們熟知的關係型數據庫,如My Sql和Oracle就是保證了可用性和數據一致性,可是他並非個分佈式系統。一旦關係型數據庫要考慮主備同步、集羣部署等就必需要把P也考慮進來。
其實,在CAP理論中。C,A,P三者並非平等的,CAP之父在《Spanner,真時,CAP理論》一文中寫到:
若是說Spanner真有什麼特別之處,那就是谷歌的廣域網。Google經過創建私有網絡以及強大的網絡工程能力來保證P,在多年運營改進的基礎上,在生產環境中能夠最大程度的減小分區發生,從而實現高可用性。
從Google的經驗中能夠獲得的結論是,沒法經過下降CA來提高P。要想提高系統的分區容錯性,須要經過提高基礎設施的穩定性來保障。
因此,對於一個分佈式系統來講。P是一個基本要求,CAP三者中,只能在CA二者之間作權衡,而且要想盡辦法提高P。
若是一個分佈式系統不要求強的可用性,即允許系統停機或者長時間無響應的話,就能夠在CAP三者中保障CP而捨棄A。
一個保證了CP而一個捨棄了A的分佈式系統,一旦發生網絡故障或者消息丟失等狀況,就要犧牲用戶的體驗,等待全部數據所有一致了以後再讓用戶訪問系統。
設計成CP的系統其實也很多,其中最典型的就是不少分佈式數據庫,他們都是設計成CP的。在發生極端狀況時,優先保證數據的強一致性,代價就是捨棄系統的可用性。如Redis、HBase等,還有分佈式系統中經常使用的Zookeeper也是在CAP三者之中選擇優先保證CP的。
不管是像Redis、HBase這種分佈式存儲系統,仍是像Zookeeper這種分佈式協調組件。數據的一致性是他們最最基本的要求。一個連數據一致性都保證不了的分佈式存儲要他有何用?
好比Zookeeper關於CAP的思考:
ZooKeeper是個CP(一致性+分區容錯性)的,即任什麼時候刻對ZooKeeper的訪問請求能獲得一致的數據結果,同時系統對網絡分割具有容錯性。可是它不能保證每次服務請求的可用性,也就是在極端環境下,ZooKeeper可能會丟棄一些請求,消費者程序須要從新請求才能得到結果。ZooKeeper是分佈式協調服務,它的職責是保證數據在其管轄下的全部服務之間保持同步、一致。因此就不難理解爲何ZooKeeper被設計成CP而不是AP特性的了。
要高可用並容許分區,則需放棄一致性。一旦網絡問題發生,節點之間可能會失去聯繫。爲了保證高可用,須要在用戶訪問時能夠立刻獲得返回,則每一個節點只能用本地數據提供服務,而這樣會致使全局數據的不一致性。
這種捨棄強一致性而保證系統的分區容錯性和可用性的場景和案例很是多。前面咱們介紹可用性的時候說到過,不少系統在可用性方面會作不少事情來保證系統的整年可用性能夠達到N個9,因此,對於不少業務系統來講,好比淘寶的購物,12306的買票。都是在可用性和一致性之間捨棄了一致性而選擇可用性。
你在12306買票的時候確定遇到過這種場景,當你購買的時候提示你是有票的(可是可能實際已經沒票了),你也正常的去輸入驗證碼,下單了。可是過了一會系統提示你下單失敗,餘票不足。這其實就是先在可用性方面保證系統能夠正常的服務,而後在數據的一致性方面作了些犧牲,會影響一些用戶體驗,可是也不至於形成用戶流程的嚴重阻塞。
可是,咱們說不少網站犧牲了一致性,選擇了可用性,這其實也不許確的。就好比上面的買票的例子,其實捨棄的只是強一致性。退而求其次保證了最終一致性。也就是說,雖然下單的瞬間,關於車票的庫存可能存在數據不一致的狀況,可是過了一段時間,仍是要保證最終一致性的。
對於多數大型互聯網應用的場景,主機衆多、部署分散,並且如今的集羣規模愈來愈大,因此節點故障、網絡故障是常態,並且要保證服務可用性達到N個9,即保證P和A,捨棄C(退而求其次保證最終一致性)。雖然某些地方會影響客戶體驗,但沒達到形成用戶流程的嚴重程度。
上面介紹瞭如何CAP中權衡及取捨以及典型的案例。孰優孰略,沒有定論,只能根據場景定奪,適合的纔是最好的。
對於涉及到錢財這樣不能有一絲讓步的場景,C必須保證。網絡發生故障寧肯中止服務,這是保證CP,捨棄A。好比前幾年支付寶光纜被挖斷的事件,在網絡出現故障的時候,支付寶就在可用性和數據一致性之間選擇了數據一致性,用戶感覺到的是支付寶系統長時間宕機,可是其實背後是無數的工程師在恢復數據,保證數數據的一致性。
在分佈式系統中,咱們每每追求的是可用性,它的重要程序比一致性要高,那麼如何實現高可用性呢? 前人已經給咱們提出來了另一個理論,就是BASE理論,它是用來對CAP定理進行進一步擴充的。BASE理論指的是:
BASE理論是對CAP中的一致性和可用性進行一個權衡的結果,理論的核心思想就是:咱們沒法作到強一致,但每一個應用均可以根據自身的業務特色,採用適當的方式來使系統達到最終一致性(Eventual consistency)。
2PC協議:一種協議,在分佈式系統保證事務的原子提交
XA:分佈式事務規範
AP:Application 應用,TM:Transaction Manager事務管理器,RM:Resource Manager資源管理器。xid是一個分佈式事務標識符。
XA {START|BEGIN} xid [JOIN|RESUME] //開啓XA事務,若是使用的是XA START而不是XA BEGIN,那麼不支持[JOIN|RESUME],xid是一個惟一值,表示事務分支標識符 XA END xid [SUSPEND [FOR MIGRATE]] // 結束一個XA事務,不支持[SUSPEND [FOR MIGRATE]] XA PREPARE xid 準備提交 XA COMMIT xid [ONE PHASE] //提交,若是使用了ONE PHASE,則表示使用一階段提交。兩階段提交協議中,若是隻有一個RM參與,那麼能夠優化爲一階段提交 XA ROLLBACK xid //回滾 XA RECOVER [CONVERT XID] //列出全部處於PREPARE階段的XA事務
案例:
mysql> XA START 'xatest’; //其中'xatest’就是xid的值 Query OK, 0 rows affected (0.00 sec) mysql> insert into user(name) values("abc"); Query OK, 1 row affected (0.00 sec) mysql> XA END 'xatest'; Query OK, 0 rows affected (0.00 sec) mysql> XA PREPARE 'xatest'; Query OK, 0 rows affected (0.01 sec) mysql> XA COMMIT 'xatest'; Query OK, 0 rows affected (0.01 sec)
兩階段提交是處理分佈式事務的經典方法。MySQL從5.0版本開始支持,SQL Server 2005 開始支持,Oracle 7 開始支持。主要經過增長事務協調者,對事務進行全局管理。
優勢:較強的一致性,適合於對數據一致性要求比較高對場景。
缺點:
其中JAVA中的JTA(Java Transaction API)就是這種實現
3PC三階段提交(非阻塞,引入超時和準備階段)。進入階段3以後,若是協調者或者執行者由於網絡等問題,接受不到docommit請求,超時後默認都執行doCommit請求。
優勢:下降了阻塞範圍,在等待超時後協調者或參與者會中斷事務。避免了協調者單點問題,階段3中協調者出現問題時,參與者會繼續提交事務。
缺陷:在參與者收到PreCommit請求後等待最終指令,若是此時協調者沒法與參與者正常通訊,會致使參與者繼續提交事務,形成數據不一致。
TCC是一種編程模式,Try-Confirm-Cancel。本質也是2PC,只是TCC在應用層控制,數據庫只是負責第一個階段。XA在數據庫層控制兩階段提交。
三個階段以下:
嚴格遵照ACID的分佈式事務咱們稱爲剛性事務,而遵循BASE理論(基本可用:在故障出現時保證核心功能可用,軟狀態:容許中間狀態出現,最終一致性:不要求分佈式事務打成中時間點數據都是一致性的,可是保證達到某個時間點後,數據就處於了一致性了)的事務咱們稱爲柔性事務,其中TCC編程模式就屬於柔性事務。
TCC是對二階段的一個改進,try階段經過預留資源的方式避免了同步阻塞資源的狀況,可是TCC編程須要業務本身實現try,confirm,cancle方法,對業務***比較大,可是實現比較容易。
分佈式事務的基本原理本質上都是兩階段提交協議(2PC),TCC (try-confirm-cancel)其實也是一種 2PC,只不過 TCC 規定了在服務層面實現的具體細節,即參與分佈式事務的服務方和調用方至少要實現三個方法:try 方法、confirm 方法、cancel 方法。
好比下面一段代碼是下單的僞代碼,若是生成主訂單和子訂單是兩個不一樣的服務,那麼子訂單生成失敗時,要對主訂單進行手動回滾,這就是一個TCC過程。TCC就是經過代碼人爲實現了兩階段提交,不一樣的業務場景所寫的代碼都不同,複雜度也不同,所以,這種模式並不能很好地被複用。
// 遠程調用生成主訂單 Integer orderId = OrderServer.addOrder(...); if (orderId == null) { return; } try{ // 生成子訂單 OrderItemServer.addItem(...); } catch(Exception e) { // 手動刪除主訂單信息 OrderServer.deleteOrder(orderId); }
優勢: 跟2PC比起來,實現以及流程相對簡單了一些,但數據的一致性比2PC也要差一些
缺點: 缺點仍是比較明顯的,在2,3步中都有可能失敗。TCC屬於應用層的一種補償方式,因此須要程序員在實現的時候多寫不少補償的代碼,在一些場景中,一些業務流程可能用TCC不太好定義及處理。
該模型其核心思想就是拆分分佈式系統中的長事務爲多個短事務,或者叫多個本地事務,而後由 Sagas 工做流引擎負責協調,若是整個流程正常結束,那麼就算是業務成功完成,若是在這過程當中實現失敗,那麼Sagas工做流引擎就會以相反的順序調用補償操做,從新進行業務回滾。
好比咱們一次關於購買旅遊套餐業務操做涉及到三個操做,他們分別是預約車輛,預約賓館,預約機票,他們分別屬於三個不一樣的遠程接口。可能從咱們程序的角度來講他們不屬於一個事務,可是從業務角度來講是屬於同一個事務的。
實現框架:https://github.com/eventuate-tram/eventuate-tram-sagas
除了Saga模式,咱們也有折中的方法,就是同步提交一個事務,異步通知其它數據源更新數據。這裏咱們放棄了事務參與者要麼不執行,要麼所有執行,採用異步方式達到最終一致性。
其核心思想是將分佈式事務拆分紅本地事務進行處理,這種思路是來源於ebay。咱們能夠從下面的流程圖中看出其中的一些細節: