分布式事務(wù)

一、本地事務(wù)

1.1 概念

本地事務(wù)必須具備ACID原則

  • 原子性(Atomicity):事務(wù)中的操作要么全部完成狈癞,要么全部不做洁灵。事務(wù)出錯會全部回滾。

  • 一致性(Consistency):事務(wù)執(zhí)行必須保證系統(tǒng)的一致性驻龟,事務(wù)前后,數(shù)據(jù)庫完整性沒有被破壞缸匪。如轉(zhuǎn)賬事務(wù)翁狐,轉(zhuǎn)賬前后總金額不變。

  • 隔離性(Isolation):事務(wù)之間不相互影響凌蔬。
    事務(wù)隔離級別:
    1.讀未提交:事務(wù)讀不阻塞其他事務(wù)讀和寫露懒,事務(wù)寫阻塞其他事務(wù)寫但不阻塞讀∩靶模可以通過寫操作加“持續(xù)-X鎖”實(shí)現(xiàn)懈词。
    臟讀
    2.讀已提交:事務(wù)讀不會阻塞其他事務(wù)讀和寫,事務(wù)寫會阻塞其他事務(wù)讀和寫辩诞】餐洌可以通過寫操作加“持續(xù)-X”鎖,讀操作加“臨時-S鎖”實(shí)現(xiàn)。
    不可重復(fù)讀
    3.可重復(fù)讀:事務(wù)讀會阻塞其他事務(wù)寫但不阻塞讀抠忘,事務(wù)寫會阻塞其他事務(wù)讀和寫撩炊。
    可以通過寫操作加“持續(xù)-X”鎖,讀操作加“持續(xù)-S鎖”實(shí)現(xiàn)褐桌。
    臟讀
    4.可串行化:事務(wù)讀寫會阻塞其他事務(wù)對整張表的寫但不阻塞讀衰抑,事務(wù)寫會阻塞其他事務(wù)對整張表象迎。使用表級鎖荧嵌。

  • 持久性(Durability):事務(wù)一旦提交,事務(wù)對所有的變更就完全保存在了數(shù)據(jù)庫中砾淌,即使系統(tǒng)宕機(jī)也不丟失啦撮。

數(shù)據(jù)庫鎖
共享鎖和排他鎖都是悲觀鎖的實(shí)現(xiàn):
共享鎖(-S鎖、讀鎖):持有S鎖的事務(wù)只讀不可寫汪厨。如果事務(wù)A對數(shù)據(jù)D加上S鎖后赃春,其它事務(wù)只能對D加上S鎖而不能加X鎖。
排他鎖(-X鎖劫乱、寫鎖):持有X鎖的事務(wù)可讀可寫织中。如果事務(wù)A對數(shù)據(jù)D加上X鎖后,其它事務(wù)不能再對D加鎖衷戈,直到A對D的鎖解除狭吼。

加鎖實(shí)例:

set  autocommit=0;  #設(shè)置mysql為非自動提交
SELECT * from city where id = "1" lock in share mode; #加共享鎖
commit;  #提交

update,insert,delete語句會自動加排它鎖

1.2 ACID實(shí)現(xiàn)原理

原子性和持久性通過undo日志和redo日志實(shí)現(xiàn);一致性通過代碼邏輯實(shí)現(xiàn)殖妇;
Undo Log和Redo Log
在操作任何數(shù)據(jù)之前刁笙,將數(shù)據(jù)備份到Undo Log緩存中,操作數(shù)據(jù)后記錄到Redo Log緩存中谦趣。將Undo Log和Redo Log寫入到磁盤中疲吸。提交事務(wù)后,異步將Redo Log日志寫入到數(shù)據(jù)庫中前鹅。
A. 事務(wù)開始
B. 記錄A=1到undo log buffer
C. 修改A=3
D. 記錄A=3到redo log buffer
E. 記錄B=2到undo log buffer
F. 修改B=4
G. 記錄B=4到redo log buffer
H. 將redo log和undo log同時寫入到磁盤
I. 事務(wù)提交(開啟線程將日志寫入數(shù)據(jù)庫)
情況一:如果在H之前宕機(jī)摘悴,那么事務(wù)異常,內(nèi)存中數(shù)據(jù)丟失舰绘,數(shù)據(jù)庫不變蹂喻。
情況二:如果在H和I之間宕機(jī),那么事務(wù)未提交且數(shù)據(jù)已經(jīng)落盤除盏,那么系統(tǒng)恢復(fù)后通過undo log日志將數(shù)據(jù)回滾窖贤。

性能問題:Redo和Undo日志通過順序?qū)?/strong>寫入到磁盤中,進(jìn)行一次IO诫惭,保證了性能佩番;如果將數(shù)據(jù)直接寫入到數(shù)據(jù)庫,因?yàn)榫鄞厮饕荁+樹結(jié)構(gòu)踱侣,是隨機(jī)寫操作粪小,IO速度較慢大磺。

1.3 Springboot本地事務(wù)

1.3.1 注解@Transactional

@Transactional

  • isolation = Isonlation.REPEATABLE_READ 指定隔離級別,mysql默認(rèn)為可重復(fù)讀探膊;
  • propagation = Propagation.REQUIRED 指定傳播行為杠愧,REQUIRED表示該方法被另一個事務(wù)方法調(diào)用,會共用一個事務(wù)逞壁,即會一起回滾流济;PROPAGATION_REQUIRES_NEW 表示不會共用一個事務(wù);
  • timeout = 2 指定事務(wù)執(zhí)行超時時間腌闯,超時回滾绳瘟;
  • rollback 指定拋出異常回滾的異常類型

手動回滾的方法:currentTransactionStatus().setRollbackOnly();

注:傳播行為分類如下

事務(wù)傳播行為類型 說明
PROPAGATION_REQUIRED 如果當(dāng)前沒有事務(wù)姿骏,就新建一個事務(wù)糖声,如果已經(jīng)存在一個事務(wù)中,加入到這個事務(wù)中分瘦。這是最常見的選擇蘸泻。
PROPAGATION_SUPPORTS 支持當(dāng)前事務(wù),如果當(dāng)前沒有事務(wù)嘲玫,就以非事務(wù)方式執(zhí)行悦施。
PROPAGATION_MANDATORY 使用當(dāng)前的事務(wù),如果當(dāng)前沒有事務(wù)趁冈,就拋出異常歼争。
PROPAGATION_REQUIRES_NEW 新建事務(wù),如果當(dāng)前存在事務(wù)渗勘,把當(dāng)前事務(wù)掛起沐绒。
PROPAGATION_NOT_SUPPORTED 以非事務(wù)方式執(zhí)行操作,如果當(dāng)前存在事務(wù)旺坠,就把當(dāng)前事務(wù)掛起乔遮。
PROPAGATION_NEVER 以非事務(wù)方式執(zhí)行,如果當(dāng)前存在事務(wù)取刃,則拋出異常蹋肮。
PROPAGATION_NESTED 如果當(dāng)前存在事務(wù),則在嵌套事務(wù)內(nèi)執(zhí)行璧疗。如果當(dāng)前沒有事務(wù)坯辩,則執(zhí)行與PROPAGATION_REQUIRED類似的操作。

1.3.2 注意點(diǎn)

    @Transactional(propagation = Propagation.REQUIRED,timeout = 2)
    public void a(){
        b();
    }

    @Transactional(propagation = Propagation.REQUIRED,timeout = 2)
    public void b(){

    }

在同一個類中不使用容器對象調(diào)用方法崩侠,而是直接調(diào)用本類中的方法會導(dǎo)致被調(diào)用的方法的事務(wù)失效漆魔。

本地事務(wù)失效:同一個對象內(nèi)事務(wù)方法互調(diào)默認(rèn)失效,原因是繞過了代理對象,事務(wù)使用代理對象來控制改抡。但是不能在類中注入自己矢炼,會產(chǎn)生循環(huán)依賴。
解決方案:

  1. 引入aop-starter(spring-boot-starter-aop)阿纤,引入了aspectj句灌。
  2. 開啟@EnableAspectJAutoProxy(exposeProxy=true);開啟aspectj動態(tài)代理功能并對外暴露代理對象欠拾。以后所有的動態(tài)代理都是aspectj創(chuàng)建的胰锌。(即是沒有接口也可以創(chuàng)建動態(tài)代理)
  3. 本類互調(diào)用調(diào)用對象


二、分布式事務(wù)

2.1 分布式事務(wù)場景

  • 從張三轉(zhuǎn)賬給李四清蚀,需要保證數(shù)據(jù)一致性匕荸,在同一個表中,可以使用Spring的@Transaction注解保證數(shù)據(jù)一致性枷邪;如果數(shù)據(jù)庫過大,進(jìn)行了水平分割诺凡,就變成了跨數(shù)據(jù)庫的事務(wù)操作东揣,此時就需要分布式事務(wù)保證數(shù)據(jù)一致性。
  • 有訂單服務(wù)和庫存服務(wù)這兩個微服務(wù)腹泌,下訂單后需要遠(yuǎn)程調(diào)用庫存服務(wù)進(jìn)行減庫存操作嘶卧,此時也需要分布式事務(wù)保證數(shù)據(jù)一致性。

異常情況

  • 遠(yuǎn)程服務(wù)假失斄垢ぁ:遠(yuǎn)程服務(wù)成功芥吟,但是因?yàn)榫W(wǎng)絡(luò)等原因返回失敗。
  • 遠(yuǎn)程服務(wù)執(zhí)行完成:多個遠(yuǎn)程服務(wù)专甩,如果后續(xù)的服務(wù)因?yàn)榫W(wǎng)絡(luò)原因失敗钟鸵,已完成的遠(yuǎn)程服務(wù)無法回滾。

2.2 分布式理論

2.2.1 CAP定理

一致性(Consistency):請求所有節(jié)點(diǎn)獲取的數(shù)據(jù)都是一致的涤躲。即分布式中一個節(jié)點(diǎn)的數(shù)據(jù)發(fā)送變化棺耍,其他節(jié)點(diǎn)需要進(jìn)行同步。
可用性(Availability):所有的節(jié)點(diǎn)都是可用的种樱。
分區(qū)容錯性(Partition tolerance):不同區(qū)域的機(jī)房通訊會產(chǎn)生網(wǎng)絡(luò)問題蒙袍。

因?yàn)榫W(wǎng)絡(luò)問題不可避免嫩挤,所以分區(qū)容錯性是必須具備的害幅。
一致性和可用性不可能同時做到,因?yàn)楸WC一致性需要進(jìn)行同步岂昭,同步未完成服務(wù)不可用以现。保證可用性導(dǎo)致接受請求時節(jié)點(diǎn)間數(shù)據(jù)未完成同步。

2.2.1.1 Eureka(滿足AP)

eureka 保證了可用性,實(shí)現(xiàn)最終一致性叼风。

Eureka各個節(jié)點(diǎn)都是平等的取董,幾個節(jié)點(diǎn)掛掉不會影響正常節(jié)點(diǎn)的工作,剩余的節(jié)點(diǎn)依然可以提供注冊和查詢服務(wù)无宿。而Eureka的客戶端在向某個Eureka注冊或時如果發(fā)現(xiàn)連接失敗茵汰,則會自動切換至其它節(jié)點(diǎn),只要有一臺Eureka還在孽鸡,就能保證注冊服務(wù)可用(保證可用性)蹂午,只不過查到的信息可能不是最新的(不保證強(qiáng)一致性),其中說明了彬碱,eureka是不滿足強(qiáng)一致性豆胸,但還是會保證最終一致性。

2.2.1.2 Zookeeper(滿足CP)

Zookeeper使用了Zab一致性選法:在選舉leader時巷疼,會停止服務(wù)晚胡,直到選舉成功之后才會再次對外提供服務(wù),這個時候就說明了服務(wù)不可用嚼沿,但是在選舉成功之后估盘,因?yàn)橐恢鞫鄰牡慕Y(jié)構(gòu),zookeeper在這時還是一個高可用注冊中心骡尽,只是在優(yōu)先保證一致性的前提下遣妥,zookeeper才會顧及到可用性。


2.2.2 Base理論

BASE 理論是對 CAP 中一致性和可用性權(quán)衡的結(jié)果:

  • 基本可用(Basically Available):分布式系統(tǒng)在出現(xiàn)不可預(yù)知故障的時攀细,允許損失部分可用性箫踩。如響應(yīng)時間和功能上的損失(降級)。
  • 軟狀態(tài)(Soft state):允許系統(tǒng)中的數(shù)據(jù)存在中間狀態(tài)谭贪,并認(rèn)為該中間狀態(tài)的存在不會影響系統(tǒng)的整體可用性境钟,即允許同步存在延時。
  • 最終一致性(Eventually consistent):最終數(shù)據(jù)會同步完成故河。

CP方式:滿足事務(wù)強(qiáng)一致吱韭,就需要在訂單服務(wù)數(shù)據(jù)庫鎖定時,對庫存服務(wù)鱼的、用戶服務(wù)數(shù)據(jù)資源同時鎖定理盆。等待三個服務(wù)業(yè)務(wù)全部處理完成,才可以釋放資源凑阶。如果此時有其他請求要操作被鎖定的資源就會阻塞猿规。
AP方式:事務(wù)滿足高可用,三個服務(wù)對應(yīng)的數(shù)據(jù)庫各自獨(dú)立執(zhí)行自己的業(yè)務(wù)宙橱,執(zhí)行本地事務(wù)姨俩,不要求相互鎖定資源蘸拔。但是訪問不同節(jié)點(diǎn)的服務(wù)會發(fā)生數(shù)據(jù)不一致的情況。最終數(shù)據(jù)會滿足一致性环葵,這就是高可用调窍、弱一致(最終一致)。

2.2.3 實(shí)現(xiàn)思路分類

2.2.3.1 剛性事務(wù)和柔性事務(wù)

滿足ACID的是剛性事務(wù)张遭,如2PC
滿足Base理論的是柔性事務(wù)邓萨,如TCC、本地消息表等菊卷。

2.2.3.2 補(bǔ)償型和通知型

補(bǔ)償型事務(wù)又分TCC缔恳、Saga,通知型事務(wù)分事務(wù)消息洁闰、最大努力通知型歉甚。
補(bǔ)償型事務(wù)都是同步的,通知型事務(wù)都是異步的扑眉。


2.3 具體實(shí)現(xiàn)

由以上理論延伸出的分布式事務(wù)解決方案:

  • XA
  • TCC
  • Saga
  • AT
  • 可靠消息最終一致(包括本地消息表和事務(wù)消息)
  • 最大努力通知

2.3.1 2PC 兩階段提交(不適合高并發(fā))

2.3.1.1 原理

遵循XA協(xié)議的數(shù)據(jù)庫支持2PC分布式事務(wù)纸泄。
兩階段提交(Tow-phase Commit,2PC)襟雷,通過引入?yún)f(xié)調(diào)者(Coordinator)來協(xié)調(diào)參與者的行為刃滓,并最終決定這些參與者是否要真正執(zhí)行事務(wù)。原理類似一致性算法raft耸弄,但是需要所有子事務(wù)都提交完成。

該模型包括的角色:

  • 應(yīng)用程序(AP):微服務(wù)
  • 事務(wù)管理器(TM):全局事務(wù)管理者
  • 資源管理器(RM):一般是數(shù)據(jù)庫
  • 通信資源管理器(CRM):是TM和RM間的通信中間件

分布式事務(wù)拆分為許多本地事務(wù)運(yùn)行在AP和RM上卓缰。每個本地事務(wù)的ACID很好實(shí)現(xiàn)计呈,全局的事務(wù)需要本地事務(wù)之間進(jìn)行通訊。XA就是X/Open DTP中通信中間件CRM與事務(wù)管理器TM間聯(lián)系的接口規(guī)范征唬,定義了用于通知事物開始捌显、提交、中止总寒、回滾等接口扶歪,各個數(shù)據(jù)庫廠商都必須實(shí)現(xiàn)這些接口。

二階段提交協(xié)議:

  • 階段一:準(zhǔn)備階段摄闸,各個本地事務(wù)完成本地事務(wù)的準(zhǔn)備工作善镰。
  • 階段二:執(zhí)行階段,各個本地事務(wù)根據(jù)上一階段執(zhí)行結(jié)果年枕,進(jìn)行提價或回滾炫欺。
    coordinator是協(xié)調(diào)者,voter是參與者熏兄。如果參與者都成功執(zhí)行了操作品洛,通知協(xié)調(diào)者進(jìn)行所有事務(wù)的提交树姨。如果其中一個參與者發(fā)生錯誤,則所有事務(wù)進(jìn)行回滾桥状。


    2PC

2.3.1.2 存在的問題

  1. 同步阻塞:準(zhǔn)備階段和提交階段都會為數(shù)據(jù)庫上鎖帽揪,直到分布式事務(wù)結(jié)束。無法滿足高并發(fā)場景辅斟。
  2. 單點(diǎn)故障:如果協(xié)調(diào)者coordinator宕機(jī)了转晰,那么等待提交事務(wù)的參與者voter就會給數(shù)據(jù)庫上鎖,導(dǎo)致后續(xù)訪問數(shù)據(jù)庫的線程阻塞砾肺。
  3. 數(shù)據(jù)不一致:在階段二挽霉,如果協(xié)調(diào)者只發(fā)送了部分Commit消息,此時網(wǎng)絡(luò)發(fā)生異常变汪,那么只有部分參與者接收到Commit消息并提交了事務(wù)侠坎,導(dǎo)致數(shù)據(jù)不一致。
  4. 太過保守裙盾,一個節(jié)點(diǎn)異常就會導(dǎo)致整個事務(wù)失敗实胸,沒有完善的容錯機(jī)制。
  5. 許多數(shù)據(jù)庫不支持或支持不理想番官。如mysql的XA實(shí)現(xiàn)庐完,沒有記錄prepare階段日志,主備切換會導(dǎo)致主庫與備庫數(shù)據(jù)不一致徘熔。


2.3.2 TCC補(bǔ)償事務(wù) ( 嚴(yán)選门躯,阿里,螞蟻金服)

2.3.2.1 原理

TCC其實(shí)就是采用補(bǔ)償機(jī)制酷师,核心思想是:針對每個操作讶凉,都要注冊一個與其對應(yīng)的確認(rèn)和補(bǔ)償(撤銷)操作,分為三階段:

  • 準(zhǔn)備階段(try):資源的監(jiān)測和預(yù)留山孔。
  • Confirm階段主要是對業(yè)務(wù)系統(tǒng)做確認(rèn)提交懂讯,Try階段執(zhí)行成功并開始執(zhí)行Confirm階段時,默認(rèn)Confirm階段是不會出錯的台颠。即:只要Try成功褐望,Confirm一定成功。
  • Cancel階段主要在業(yè)務(wù)執(zhí)行錯誤串前,需要回滾狀態(tài)下執(zhí)行的業(yè)務(wù)取消瘫里,預(yù)留資源釋放。
TCC

實(shí)例如下
對賬號A進(jìn)行扣款

  • 準(zhǔn)備階段(try):檢查余額是否充足酪呻,添加凍結(jié)資金到凍結(jié)資金字段中减宣,提交事務(wù)并通知協(xié)調(diào)者。
  • 執(zhí)行階段(confirm/cancel):
    *confirm提交:真正扣款玩荠,把凍結(jié)資金從余額中扣除漆腌,凍結(jié)資金清空贼邓。
    *cancel取消:將凍結(jié)金額清空。


    實(shí)例

2.3.2.2 優(yōu)缺點(diǎn)

優(yōu)勢
TCC執(zhí)行的每個階段的每個事務(wù)都會提交本地事務(wù)并釋放鎖闷尿,無需等待其他事務(wù)的執(zhí)行結(jié)果塑径,執(zhí)行效率高。
缺陷

  • 代碼入侵:需要人為編寫代碼實(shí)現(xiàn)try填具、confirm统舀、cancel。
  • 開發(fā)成本高:一個業(yè)務(wù)需要拆分為三步劳景,即凍結(jié)(try)誉简、扣除凍結(jié)(confirm)、釋放凍結(jié)(cancel)盟广。


2.3.3 Saga模式

2.3.3.1 原理

Saga 是一種補(bǔ)償協(xié)議闷串,在 Saga 模式下,分布式事務(wù)內(nèi)有多個參與者筋量,每一個參與者都是一個沖正補(bǔ)償服務(wù)烹吵,需要用戶根據(jù)業(yè)務(wù)場景實(shí)現(xiàn)其正向操作和逆向回滾操作。


T1T3都是正向的業(yè)務(wù)流程桨武,都對應(yīng)著一個沖正逆向操作C1C3

分布式事務(wù)執(zhí)行過程中肋拔,依次執(zhí)行各參與者的正向操作,如果所有正向操作均執(zhí)行成功呀酸,那么分布式事務(wù)提交凉蜂。如果任何一個正向操作執(zhí)行失敗,那么分布式事務(wù)會退回去執(zhí)行前面各參與者的逆向回滾操作性誉,回滾已提交的參與者跃惫,使分布式事務(wù)回到初始狀態(tài)。

Saga 正向服務(wù)與補(bǔ)償服務(wù)也需要業(yè)務(wù)開發(fā)者實(shí)現(xiàn)艾栋。因此是業(yè)務(wù)入侵的。

Saga 模式下分布式事務(wù)通常是由事件驅(qū)動的蛉顽,各個參與者之間是異步執(zhí)行的蝗砾,Saga 模式是一種長事務(wù)解決方案。

Saga 模式使用場景
Saga 模式適用于業(yè)務(wù)流程長且需要保證事務(wù)最終一致性的業(yè)務(wù)系統(tǒng)携冤,Saga 模式一階段就會提交本地事務(wù)悼粮,無鎖、長流程情況下可以保證性能曾棕。
事務(wù)參與者可能是其它公司的服務(wù)或者是遺留系統(tǒng)的服務(wù)扣猫,無法進(jìn)行改造和提供 TCC 要求的接口,可以使用 Saga 模式翘地。

2.3.3.2 優(yōu)缺點(diǎn)

優(yōu)點(diǎn)

  • 一階段提交本地數(shù)據(jù)庫事務(wù)申尤,無鎖癌幕,高性能;
  • 參與者可以采用事務(wù)驅(qū)動異步執(zhí)行昧穿,高吞吐
  • 補(bǔ)償服務(wù)即正向服務(wù)的“反向”勺远,易于理解,易于實(shí)現(xiàn)时鸵;

缺點(diǎn)
Saga 模式由于一階段已經(jīng)提交本地數(shù)據(jù)庫事務(wù)胶逢,且沒有進(jìn)行“預(yù)留”動作,所以不能保證隔離性饰潜。后續(xù)會講到對于缺乏隔離性的應(yīng)對措施初坠。

注意
與TCC實(shí)踐經(jīng)驗(yàn)相同的是,Saga 模式中彭雾,每個事務(wù)參與者的沖正碟刺、逆向操作,需要支持:

  • 空補(bǔ)償:逆向操作早于正向操作時冠跷;
  • 防懸掛控制:空補(bǔ)償后要拒絕正向操作
  • 冪等

參考文章:http://www.reibang.com/p/e4b662407c66


2.3.4 AT模式

Seata開源的AT模式南誊,無侵入式分布式事務(wù)解決方案。是對TCC和2PC的模型優(yōu)化蜜托,解決TCC模式中代碼侵入抄囚、編碼復(fù)制等問題。


流程圖

流程
一階段:執(zhí)行本地事務(wù)并返回執(zhí)行結(jié)果橄务。Seata會攔截業(yè)務(wù)SQL并解析幔托,將需要修改的記錄保存成before image,然后執(zhí)行業(yè)務(wù)SQL蜂挪,將業(yè)務(wù)更新后的記錄存儲為after image重挑,最后獲取全局行鎖,提交事務(wù)棠涮。這些都是本地事務(wù)谬哀,保證了原子性。
二階段:

  • 如果是提交的話严肪,只需要刪除日志和刪除行鎖即可史煎。
  • 如果是回滾,Seata需要根據(jù)before image日志回滾數(shù)據(jù)驳糯,回滾前需要讀取數(shù)據(jù)庫記錄和after image日志對比保證沒有其他事務(wù)對記錄進(jìn)行操作篇梭,兩份數(shù)據(jù)一致,說明沒有臟寫酝枢,還原業(yè)務(wù)數(shù)據(jù)恬偷,如果不一致,需要轉(zhuǎn)人工處理帘睦。


    流程圖

詳情見http://www.reibang.com/writer#/notebooks/44681510/notes/69583976


2.3.5 可靠消息一致性 (注冊送積分袍患,登錄送優(yōu)惠券)

兩種實(shí)現(xiàn)

  • 本地消息表
  • 事務(wù)消息

2.3.5.1 本地消息表

2.3.5.1.1 原理

它的核心思想是將需要分布式處理的任務(wù)通過消息或者日志的方式來異步執(zhí)行坦康,消息或日志可以存到本地文件、數(shù)據(jù)庫或消息隊(duì)列协怒,再通過業(yè)務(wù)規(guī)則進(jìn)行失敗重試涝焙,它要求各服務(wù)的接口是冪等的。

本地消息表與業(yè)務(wù)數(shù)據(jù)表處于同一個數(shù)據(jù)庫中孕暇,這樣就能利用本地事務(wù)來保證在對這兩個表的操作滿足事務(wù)特性仑撞,并且使用了消息隊(duì)列來保證最終一致性

在分布式事務(wù)操作的一方完成寫業(yè)務(wù)數(shù)據(jù)的操作之后向本地消息表發(fā)送一個消息,本地事務(wù)能保證這個消息一定會被寫入本地消息表中妖滔;
之后將本地消息表中的消息轉(zhuǎn)發(fā)到 Kafka 等消息隊(duì)列中隧哮,如果轉(zhuǎn)發(fā)成功則將消息從本地消息表中刪除,否則繼續(xù)重新轉(zhuǎn)發(fā)座舍;
消息消費(fèi)方處理這個消息沮翔,并完成自己的業(yè)務(wù)邏輯。此時如果本地事務(wù)處理成功曲秉,表明已經(jīng)處理成功了采蚀,如果處理失敗,那么就會重試執(zhí)行承二。如果是業(yè)務(wù)上面的失敗榆鼠,可以給生產(chǎn)方發(fā)送一個業(yè)務(wù)補(bǔ)償消息,通知生產(chǎn)方進(jìn)行回滾等操作(凍結(jié)或者補(bǔ)償)亥鸠;

簡化版本
2.3.5.1.2 優(yōu)缺點(diǎn)

優(yōu)點(diǎn)
與TCC相比妆够,實(shí)現(xiàn)方式簡單,開發(fā)成本低负蚊。
缺陷

  • 被動業(yè)務(wù)執(zhí)行時間不確定神妹,時效性差;
  • 需要處理被動業(yè)務(wù)方的冪等問題家妆;
  • 被動業(yè)務(wù)失敗不會導(dǎo)致主動業(yè)務(wù)回滾鸵荠,需要額外策略來回滾主動業(yè)務(wù);
  • 代碼耦合性較高伤极,需要消息表和消息確認(rèn)回調(diào)方法腰鬼。
2.3.5.1.3 實(shí)例


流程:

  1. 可靠生產(chǎn)(確保消息投遞到MQ中):
    ①創(chuàng)建冗余業(yè)務(wù)數(shù)據(jù)表,將需要事務(wù)修改的數(shù)據(jù)添加到冗余表中并設(shè)置狀態(tài)為0塑荒,然后發(fā)送消息到MQ中。
    ②利用RabbitMQ的publisher/Confirm機(jī)制開啟確認(rèn)機(jī)制后姜挺,如果消息正常發(fā)送到MQ中就會獲取到回執(zhí)信息齿税,然后將狀態(tài)修改為1;
    ③定義一個定時器循環(huán)檢查冗余表中未確認(rèn)的信息并發(fā)送給RabbitMQ炊豪,直到超過最大重試次數(shù)凌箕。@EnableScheduling和@Scheduled(cron="")創(chuàng)建定時任務(wù)拧篮。
  2. 可靠消費(fèi)(確保消息)
    ①通過異常拒收,然后進(jìn)入死信隊(duì)列再次消費(fèi)牵舱,如果再次異常則進(jìn)行報警串绩。兩次消費(fèi)確保可靠消費(fèi)芜壁。
    ②可能存在重復(fù)消費(fèi)導(dǎo)致冪等性失效的情況礁凡,需要保證冪等性。通過數(shù)據(jù)庫唯一索引或分布式鎖來保證冪等性慧妄。

代碼見Github倉庫顷牌,點(diǎn)擊此處


2.3.5.2 MQ事務(wù)消息

2.3.5.2.1 原理

原理類似本地消息表,但是需要MQ支持半消息機(jī)制或者類似特性(如RocketMQ)塞淹。
簡單原理:本地消息表通過冗余表重復(fù)投遞未確認(rèn)的消息窟蓝,而MQ事務(wù)消息會在消息到達(dá)MQ時進(jìn)行本地事務(wù)操作,如果成功則繼續(xù)發(fā)送饱普,否則撤銷發(fā)送运挫。這樣可以省下消息表和重復(fù)發(fā)送消息

半消息:指的是暫不能投遞的消息套耕,發(fā)送方已經(jīng)將消息成功發(fā)送到了 MQ 服務(wù)端谁帕,但是服務(wù)端未收到生產(chǎn)者對該消息的二次確認(rèn)。
消息回查:由于網(wǎng)絡(luò)閃斷箍铲、生產(chǎn)者應(yīng)用重啟等原因雇卷,導(dǎo)致某條事務(wù)消息的二次確認(rèn)丟失,MQ 服務(wù)端通過掃描發(fā)現(xiàn)某條消息長期處于“半消息”時颠猴,需要主動向消息生產(chǎn)者詢問該消息的最終狀態(tài)(Commit 或是 Rollback)关划,該過程即消息回查。

image.png
  1. 事務(wù)發(fā)起方首先發(fā)送半消息到MQ翘瓮;
  2. MQ通知發(fā)送方消息發(fā)送成功贮折;
  3. 在發(fā)送半消息成功后執(zhí)行本地事務(wù);
  4. 根據(jù)本地事務(wù)執(zhí)行結(jié)果返回commit或者是rollback资盅;
  5. 如果消息是rollback, MQ將丟棄該消息不投遞调榄;如果是commit,MQ將會消息發(fā)送給消息訂閱方呵扛;
  6. 訂閱方根據(jù)消息執(zhí)行本地事務(wù)每庆;
  7. 訂閱方執(zhí)行本地事務(wù)成功后再從MQ中將該消息標(biāo)記為已消費(fèi);
  8. 如果執(zhí)行本地事務(wù)過程中今穿,執(zhí)行端掛掉缤灵,或者超時,MQ服務(wù)器端將不停的詢問producer來獲取事務(wù)狀態(tài);
  9. Consumer端的消費(fèi)成功機(jī)制有MQ保證腮出;

2.3.5.2.1 MQ事務(wù)消息對比本地消息表

①DB本地消息表:

  • 使用了數(shù)據(jù)庫來存儲事務(wù)消息帖鸦,降低了對MQ的要求,但是增加了存儲成本胚嘲;
  • 事務(wù)消息使用了異步投遞作儿,增大了消息重復(fù)投遞的可能性;

②MQ事務(wù)消息:

  • 需要MQ支持半消息機(jī)制或者類似特性馋劈,在重復(fù)投遞上具有比較好的去重處理攻锰;
  • 具有比較大的業(yè)務(wù)侵入性,需要業(yè)務(wù)方進(jìn)行改造侣滩,提供對應(yīng)的本地操作成功的回查功能口注;


2.3.6 最大努力通知(銀行通知、支付結(jié)果通知)

2.3.6.1 原理

按規(guī)律進(jìn)行通知君珠,不保證消息一定通知成功寝志,但會提供可查詢操作接口進(jìn)行核對。這種方案主要用在與第三方系統(tǒng)通訊時策添,如調(diào)用第三方支付平臺(微信或支付寶)支付后的支付結(jié)果異步返回材部。

image.png

  1. 業(yè)務(wù)活動的主動方,在完成業(yè)務(wù)處理之后唯竹,向業(yè)務(wù)活動的被動方發(fā)送消息乐导,允許消息丟失。
  2. 主動方可以設(shè)置時間階梯型通知規(guī)則浸颓,在通知失敗后按規(guī)則重復(fù)通知物臂,直到通知N次后不再通知。
  3. 主動方提供校對查詢接口給被動方按需校對查詢产上,用于恢復(fù)丟失的業(yè)務(wù)消息棵磷。
  4. 業(yè)務(wù)活動的被動方如果正常接收了數(shù)據(jù),就正常返回響應(yīng)晋涣,并結(jié)束事務(wù)仪媒。
  5. 如果被動方?jīng)]有正常接收,根據(jù)定時策略谢鹊,向業(yè)務(wù)活動主動方查詢算吩,恢復(fù)丟失的業(yè)務(wù)消息。

特點(diǎn)

  1. 用到的服務(wù)模式:可查詢操作佃扼、冪等操作偎巢;
  2. 被動方的處理結(jié)果不影響主動方的處理結(jié)果;
  3. 適用于對業(yè)務(wù)最終一致性的時間敏感度低的系統(tǒng)兼耀;
  4. 適合跨企業(yè)的系統(tǒng)間的操作艘狭,或者企業(yè)內(nèi)部比較獨(dú)立的系統(tǒng)間的操作挎扰,比如銀行通知、商戶通知等巢音;

2.3.6.2最大努力通知對比DB本地消息表和MQ事務(wù)消息

  • 最大努力通知 允許發(fā)起通知方處理業(yè)務(wù)失敗,發(fā)起通知方需提供查詢執(zhí)行情況接口尽超。需要知道消息的相關(guān)信息官撼,可以在超時未收到消息時通過接口進(jìn)行查詢。
  • DB本地消息表和MQ事務(wù)消息 是為了保證了分布式業(yè)務(wù)能夠順利執(zhí)行似谁。


2.4 總結(jié):

屬性 2PC TCC Saga 本地消息表 事務(wù)消息 最大努力通知
事務(wù)一致性 強(qiáng)一致 最終一致 最終一致 最終一致 最終一致 最終一致
性能
業(yè)務(wù)侵入性
復(fù)雜性
使用局限性
維護(hù)成本

總結(jié) XA傲绣、TCC、Saga巩踏、AT 模式分析:

  • XA模式是分布式強(qiáng)一致性的解決方案秃诵,但性能低而使用較少。
  • TCC 模式是高性能分布式事務(wù)解決方案塞琼,適用于核心系統(tǒng)等對性能有很高要求的場景菠净。
  • Saga 模式是長事務(wù)解決方案,適用于業(yè)務(wù)流程長且需要保證事務(wù)最終一致性的業(yè)務(wù)系統(tǒng)彪杉,Saga 模式一階段就會提交本地事務(wù)毅往,無鎖,長流程情況下可以保證性能派近,多用于渠道層攀唯、集成層業(yè)務(wù)系統(tǒng)。事務(wù)參與者可能是其它公司的服務(wù)或者是遺留系統(tǒng)的服務(wù)渴丸,無法進(jìn)行改造和提供 TCC 要求的接口侯嘀,也可以使用 Saga 模式。
  • AT 模式是無侵入的分布式事務(wù)解決方案谱轨,適用于不希望對業(yè)務(wù)進(jìn)行改造的場景戒幔,幾乎0學(xué)習(xí)成本。

測試結(jié)果:
在單機(jī)環(huán)境下使用 jmeter 進(jìn)行壓測碟嘴,
Seata AT模式:在100并發(fā)下成功數(shù)是 23溪食、29,成功率不到30%娜扇,在1000并發(fā)下成功數(shù)是276错沃、260,成功率同樣是不到30%;
Seata TCC模式:在100并發(fā)下成功數(shù) 98雀瓢、80枢析,500并發(fā)下成功數(shù)是304,在1000并發(fā)下成功數(shù)是481;

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
禁止轉(zhuǎn)載刃麸,如需轉(zhuǎn)載請通過簡信或評論聯(lián)系作者醒叁。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子把沼,更是在濱河造成了極大的恐慌啊易,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,482評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件饮睬,死亡現(xiàn)場離奇詭異租谈,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)捆愁,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,377評論 2 382
  • 文/潘曉璐 我一進(jìn)店門割去,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人昼丑,你說我怎么就攤上這事呻逆。” “怎么了菩帝?”我有些...
    開封第一講書人閱讀 152,762評論 0 342
  • 文/不壞的土叔 我叫張陵咖城,是天一觀的道長。 經(jīng)常有香客問我胁附,道長酒繁,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,273評論 1 279
  • 正文 為了忘掉前任控妻,我火速辦了婚禮州袒,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘弓候。我一直安慰自己郎哭,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,289評論 5 373
  • 文/花漫 我一把揭開白布菇存。 她就那樣靜靜地躺著夸研,像睡著了一般。 火紅的嫁衣襯著肌膚如雪依鸥。 梳的紋絲不亂的頭發(fā)上亥至,一...
    開封第一講書人閱讀 49,046評論 1 285
  • 那天,我揣著相機(jī)與錄音贱迟,去河邊找鬼姐扮。 笑死,一個胖子當(dāng)著我的面吹牛衣吠,可吹牛的內(nèi)容都是我干的茶敏。 我是一名探鬼主播,決...
    沈念sama閱讀 38,351評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼缚俏,長吁一口氣:“原來是場噩夢啊……” “哼惊搏!你這毒婦竟也來了贮乳?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,988評論 0 259
  • 序言:老撾萬榮一對情侶失蹤恬惯,失蹤者是張志新(化名)和其女友劉穎向拆,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體酪耳,經(jīng)...
    沈念sama閱讀 43,476評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡亲铡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,948評論 2 324
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了葡兑。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,064評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡赞草,死狀恐怖讹堤,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情厨疙,我是刑警寧澤洲守,帶...
    沈念sama閱讀 33,712評論 4 323
  • 正文 年R本政府宣布,位于F島的核電站沾凄,受9級特大地震影響梗醇,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜撒蟀,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,261評論 3 307
  • 文/蒙蒙 一叙谨、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧保屯,春花似錦手负、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,264評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至切蟋,卻和暖如春统捶,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背柄粹。 一陣腳步聲響...
    開封第一講書人閱讀 31,486評論 1 262
  • 我被黑心中介騙來泰國打工喘鸟, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人镰惦。 一個月前我還...
    沈念sama閱讀 45,511評論 2 354
  • 正文 我出身青樓迷守,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,802評論 2 345

推薦閱讀更多精彩內(nèi)容