XA模式
XA模式分為兩種情況:提交成功:
提交失敗:
具有強一致性seata相當于是在RM上做了一層封裝;
XA模式
優(yōu)點
:
1.事務的強一致性,只要有失敗的,TC事務協調者就會發(fā)送信息讓RM回滾——>滿足ACID原則
2.沒有代碼侵入,常用數據庫都支持缺點
:
1.第一階段就要鎖定數據庫資源,但是卻不提交,從而導致數據庫所占用的資源不能釋放(占數據庫鎖),性能較差
2.依賴關系型數據庫實現事務
實現XA模式
步驟:
1.在yaml文件中開啟XA代理模式
2.添加@GlobalTransactional注解開啟全局事務
AT模式
利用快照來保證事務的一致性,來進行數據回滾;
AT模式是一種最終一致的模式
:因為RM資源管理器執(zhí)行sql后會直接提交,那么此時如果是數據不一致的情況下,那么說明肯定是軟一致,但是在階段二時,AT模式RM資源管理器會利用快照進行數據回滾,從而保證最終一致;
AT模式直接提交,有利于提高效率
AT模式的臟寫問題(對同數據并發(fā)寫的問題)
臟寫問題
:造成數據空轉現象
什么是臟寫:
簡而言之,就是兩個事務并發(fā)執(zhí)行,修改同一條數據,我第一個事務修改并且提交之后,釋放DB鎖資源,第二個事務想要進行回滾,那么就會導致臟寫——>前一個事務修改無效
解決
:對事務2造成數據空轉現象進行處理
1.事務1先獲取DB鎖,并且保存快照
——>2.然后執(zhí)行業(yè)務sql,我們在提交事務前獲取全局鎖(防止一提交事務,釋放DB鎖后,其他事務立馬插入獲取DB鎖更改sql)
——>3.此時全局鎖會記錄操作當前數據的事務,讓該事務持有全局鎖,然后提交事務釋放DB鎖
——>4.此時其他事務可以爭奪DB鎖,執(zhí)行業(yè)務sql
——>5.然后和之前一樣,它也要獲取全局鎖,但是全局鎖此時已經被事務1拿了,所以它會進行自旋(300ms)
——>6.然后事務1如果此時要根據快照恢復數據,那么就需要DB鎖,但是DB鎖此時被其他事務拿了
——>7.死鎖現象發(fā)生
——>8.還好其他事務重試失敗后會釋放鎖資源,因為獲取全局鎖失敗,那么后面的事務提交也進行不了
——>9.事務1再次拿到DB鎖,可以進行快照恢復數據了;
其他事務不獲取全局鎖的一個情況(AT模式寫隔離的實現)
利用了CAS的思想 :
實際上是有兩份快照的:before-image、after-image
跟cas一樣,before是我們要回滾目標的狀態(tài),而after是相當于驗證的一個狀態(tài),如果滿足after的內容,就可以設置為before;
如果不一樣不滿足的話,就會判斷不能恢復回滾,那么我們可以記錄異常發(fā)送警告;
總結:
AT:在第一階段RM直接提交事務,釋放數據庫資源,不需要像XA模式那樣,還需要將狀態(tài)返回給TC事務協調者,
還利用了全局鎖實現讀寫分離
:將表執(zhí)行的事務儲存起來,相當于一個標識;
并且**沒有代碼侵入,seata自動完成回滾和提交——>seata相當于RM資源管理器的一個代理
**
實現AT模式
數據庫表中:lock_table:全局鎖,undo_log:放的是快照信息
可以再下單途中在業(yè)務代碼中加上斷點的方式查看數據庫表中記錄的快照信息和AT模式的全局鎖信息 他們會在業(yè)務結束時自動銷毀清理干凈
TCC模式
那我們TCC模式是怎么保證一致性?
首先我們想想AT模式,在第一階段,通過對數據庫鎖的獲取完成事務,事務都是隔離的,所以有人成功有人失敗,只有在二階段完成回滾才能夠保證數據的最終一致性,中間還是出現了軟狀態(tài);
而TCC模式
,為什么就不需要鎖了呢?我們AT模式是利用全局鎖來保證一致性的——>執(zhí)行sql后提交前上一道全局鎖,那么其他事務的sql就執(zhí)行不了進行自旋,超時就釋放DB鎖,而TCC解決
:利用了每個事務都是預留資源進行處理
——> 第一個事務凍結的金額和第二個事務凍結的金額是不一樣的,跟其他事務是沒有關系的,那么回滾事務也是跟其他事務不影響的,不需要加鎖(類似Semaphore)
簡而言之就是把事務所用到的資源預留起來 等后面的結果再來判斷是扣除還是釋放,預留起來后數據庫原表中的數據已經扣除了,所以其他的業(yè)務請求也不會有影響
TCC模式的關鍵在于有代碼侵入:需要考慮Comfirm成功提交和Cancel數據回滾的編寫
優(yōu)點:1.TCC第一階段直接提交事務,提交完直接釋放數據庫資源,AT的話也是直接執(zhí)行,但是使用了全局鎖來保存事務操作的一個狀態(tài),保證其他事務爭奪不了,XA的話第一階段就垃圾了,不會提交sql業(yè)務,需要把狀態(tài)給到TC事務協調者進行判斷是否回滾還是提交(是一提交或者回滾就是全局那種);
2.無需生成快照與全局鎖,依賴的是一個補償操作,因為事務直接提交的原則,所以其他事務是操作不到自己的,可用于非關系型數據庫
TCC實現
具體模式還得根據場景來,比如TCC,就很像Semaphore,一般來說是對一個共享資源進行操作,比如停車場的停車位,庫存…,像下單服務就不適合了,因為你每次調用都是一個新的訂單;
一個事務是可以有多個模式實現的
我們怎么樣去判斷是否空回滾和業(yè)務懸掛?
利用兩者相互判斷 根據凍結金額的那張表來判斷 在進行try業(yè)務前 先查詢一下凍結金額的表中的數據是否為空 如果不為空則證明已經執(zhí)行了CANCEL操作 則需要直接拒絕try的操作,反之在進行cancel業(yè)務前,需要根據事務id查詢一下凍結金額的表中的數據是否為空 如果為空的話則證明try業(yè)務還沒做,需要進行空回滾,同時也需要記錄數據,new一個新的對象將凍結金額設為0,以及其他數據set進去
業(yè)務分析
我們可以在BusinessActionContext中獲取里面的參數
事務表:表示事務凍結金額,凍結金額狀態(tài)發(fā)生改變——>表示那部分被鎖定
事務id,用戶id,凍結金額和狀態(tài)
業(yè)務方便代碼的實現
:
@Slf4j
@Service
public class AccountTCCServiceImpl implements AccountTCCService {
@Autowired
private AccountMapper accountMapper;
@Autowired
private AccountFreezeMapper accountFreezeMapper;
@Override
@Transactional
public void deduct(String userId, int money) {
//0.獲取事務id
String xid = RootContext.getXID();
// 判斷是否有凍結記錄 有的話直接拒絕執(zhí)行try業(yè)務
AccountFreeze Freeze = accountFreezeMapper.selectById(xid);
if (Freeze!=null) {
//拒絕
return;
}
//1.扣減可用余額
accountMapper.deduct(userId, money);
//2.記錄凍結金額,記錄事務狀態(tài)
AccountFreeze accountFreeze = new AccountFreeze();
accountFreeze.setUserId(userId);
accountFreeze.setFreezeMoney(money);
accountFreeze.setState(AccountFreeze.State.TRY);
accountFreeze.setXid(xid);
accountFreezeMapper.insert(accountFreeze);
}
@Override
public boolean confirm(BusinessActionContext ctx) {
//0.獲取事務id
String xid = ctx.getXid();
//1.刪除數據
int count = accountFreezeMapper.deleteById(xid);
return count == 1;
}
@Override
public boolean cancel(BusinessActionContext ctx) {
//0.查詢凍結記錄
AccountFreeze accountFreeze = accountFreezeMapper.selectById(ctx.getXid());
String userId = (String) ctx.getActionContext("userId");
//0.2.判斷是否空回滾
if (accountFreeze == null){
accountFreeze = new AccountFreeze();
accountFreeze.setUserId(userId);
accountFreeze.setFreezeMoney(0);
accountFreeze.setState(AccountFreeze.State.CANCEL);
accountFreeze.setXid(ctx.getXid());
accountFreezeMapper.insert(accountFreeze);
return true;
}
//0.3 冪等判斷
if (accountFreeze.getState()==AccountFreeze.State.CANCEL){
//已經處理過cancel了 無需重復業(yè)務
return true;
}
//1.恢復可用余額
accountMapper.refund(accountFreeze.getUserId(),accountFreeze.getFreezeMoney());
//2.將凍結金額清零 改狀態(tài)為cancel
accountFreeze.setFreezeMoney(0);
accountFreeze.setState(AccountFreeze.State.CANCEL);
int count = accountFreezeMapper.updateById(accountFreeze);
return count == 1;
}
}
Saga模式
與TCC模式類似,但是TCC第一階段只是將資源進行凍結,真正的去除還是在第二階段的,而Saga模式是直接提交本地事務,第二階段直接操作事務本身:成功則什么都不做,失敗則通過編寫補償業(yè)務來進行回滾;
與AT相比沒有用鎖,與TCC比沒有凍結資源,性能較好;
失敗用自定義的補償來寫;
缺點:
沒有保證隔離性,既沒有隔離預留資源又沒有上鎖,容易出現臟寫
文章來源:http://www.zghlxwxcb.cn/news/detail-725734.html
總結
文章來源地址http://www.zghlxwxcb.cn/news/detail-725734.html
到了這里,關于微服務13-Seata的四種分布式事務模式的文章就介紹完了。如果您還想了解更多內容,請在右上角搜索TOY模板網以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網!