一、本地事務(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)依賴。
解決方案:
- 引入aop-starter(spring-boot-starter-aop)阿纤,引入了aspectj句灌。
- 開啟@EnableAspectJAutoProxy(exposeProxy=true);開啟aspectj動態(tài)代理功能并對外暴露代理對象欠拾。以后所有的動態(tài)代理都是aspectj創(chuàng)建的胰锌。(即是沒有接口也可以創(chuàng)建動態(tài)代理)
- 本類互調(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)行回滾桥状。
2.3.1.2 存在的問題
- 同步阻塞:準(zhǔn)備階段和提交階段都會為數(shù)據(jù)庫上鎖帽揪,直到分布式事務(wù)結(jié)束。無法滿足高并發(fā)場景辅斟。
- 單點(diǎn)故障:如果協(xié)調(diào)者coordinator宕機(jī)了转晰,那么等待提交事務(wù)的參與者voter就會給數(shù)據(jù)庫上鎖,導(dǎo)致后續(xù)訪問數(shù)據(jù)庫的線程阻塞砾肺。
- 數(shù)據(jù)不一致:在階段二挽霉,如果協(xié)調(diào)者只發(fā)送了部分Commit消息,此時網(wǎng)絡(luò)發(fā)生異常变汪,那么只有部分參與者接收到Commit消息并提交了事務(wù)侠坎,導(dǎo)致數(shù)據(jù)不一致。
- 太過保守裙盾,一個節(jié)點(diǎn)異常就會導(dǎo)致整個事務(wù)失敗实胸,沒有完善的容錯機(jī)制。
- 許多數(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ù)留資源釋放。
實(shí)例如下
對賬號A進(jìn)行扣款
- 準(zhǔn)備階段(try):檢查余額是否充足酪呻,添加凍結(jié)資金到凍結(jié)資金字段中减宣,提交事務(wù)并通知協(xié)調(diào)者。
-
執(zhí)行階段(confirm/cancel):
*confirm提交:真正扣款玩荠,把凍結(jié)資金從余額中扣除漆腌,凍結(jié)資金清空贼邓。
*cancel取消:將凍結(jié)金額清空。
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)其正向操作和逆向回滾操作。
分布式事務(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í)例
流程:
- 可靠生產(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ù)拧篮。 - 可靠消費(fèi)(確保消息)
①通過異常拒收,然后進(jìn)入死信隊(duì)列再次消費(fèi)牵舱,如果再次異常則進(jìn)行報警串绩。兩次消費(fèi)確保可靠消費(fèi)芜壁。
②可能存在重復(fù)消費(fèi)導(dǎo)致冪等性失效的情況礁凡,需要保證冪等性。通過數(shù)據(jù)庫唯一索引或分布式鎖來保證冪等性慧妄。
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)关划,該過程即消息回查。
- 事務(wù)發(fā)起方首先發(fā)送半消息到MQ翘瓮;
- MQ通知發(fā)送方消息發(fā)送成功贮折;
- 在發(fā)送半消息成功后執(zhí)行本地事務(wù);
- 根據(jù)本地事務(wù)執(zhí)行結(jié)果返回commit或者是rollback资盅;
- 如果消息是rollback, MQ將丟棄該消息不投遞调榄;如果是commit,MQ將會消息發(fā)送給消息訂閱方呵扛;
- 訂閱方根據(jù)消息執(zhí)行本地事務(wù)每庆;
- 訂閱方執(zhí)行本地事務(wù)成功后再從MQ中將該消息標(biāo)記為已消費(fèi);
- 如果執(zhí)行本地事務(wù)過程中今穿,執(zhí)行端掛掉缤灵,或者超時,MQ服務(wù)器端將不停的詢問producer來獲取事務(wù)狀態(tài);
- 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é)果異步返回材部。
- 業(yè)務(wù)活動的主動方,在完成業(yè)務(wù)處理之后唯竹,向業(yè)務(wù)活動的被動方發(fā)送消息乐导,允許消息丟失。
- 主動方可以設(shè)置時間階梯型通知規(guī)則浸颓,在通知失敗后按規(guī)則重復(fù)通知物臂,直到通知N次后不再通知。
- 主動方提供校對查詢接口給被動方按需校對查詢产上,用于恢復(fù)丟失的業(yè)務(wù)消息棵磷。
- 業(yè)務(wù)活動的被動方如果正常接收了數(shù)據(jù),就正常返回響應(yīng)晋涣,并結(jié)束事務(wù)仪媒。
- 如果被動方?jīng)]有正常接收,根據(jù)定時策略谢鹊,向業(yè)務(wù)活動主動方查詢算吩,恢復(fù)丟失的業(yè)務(wù)消息。
特點(diǎn)
- 用到的服務(wù)模式:可查詢操作佃扼、冪等操作偎巢;
- 被動方的處理結(jié)果不影響主動方的處理結(jié)果;
- 適用于對業(yè)務(wù)最終一致性的時間敏感度低的系統(tǒng)兼耀;
- 適合跨企業(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;