日韩无码专区无码一级三级片|91人人爱网站中日韩无码电影|厨房大战丰满熟妇|AV高清无码在线免费观看|另类AV日韩少妇熟女|中文日本大黄一级黄色片|色情在线视频免费|亚洲成人特黄a片|黄片wwwav色图欧美|欧亚乱色一区二区三区

RELATEED CONSULTING
相關(guān)咨詢(xún)
選擇下列產(chǎn)品馬上在線(xiàn)溝通
服務(wù)時(shí)間:8:30-17:00
你可能遇到了下面的問(wèn)題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營(yíng)銷(xiāo)解決方案
分布式事務(wù),阿里為什么鐘愛(ài)TCC

分布式事務(wù),阿里為什么鐘愛(ài)TCC

作者: jinjunzhu 2021-06-08 12:46:27

開(kāi)發(fā)

架構(gòu)

分布式 分布式事務(wù)的實(shí)現(xiàn)方式中,TCC是比較知名的模式。但是我一直不喜歡這種模式,原因是這種模式有很多問(wèn)題要考慮。

創(chuàng)新互聯(lián)技術(shù)團(tuán)隊(duì)十余年來(lái)致力于為客戶(hù)提供網(wǎng)站建設(shè)、成都網(wǎng)站制作、成都品牌網(wǎng)站建設(shè)、營(yíng)銷(xiāo)型網(wǎng)站建設(shè)、搜索引擎SEO優(yōu)化等服務(wù)。經(jīng)過(guò)多年發(fā)展,公司擁有經(jīng)驗(yàn)豐富的技術(shù)團(tuán)隊(duì),先后服務(wù)、推廣了成百上千家網(wǎng)站,包括各類(lèi)中小企業(yè)、企事單位、高校等機(jī)構(gòu)單位。

本文轉(zhuǎn)載自微信公眾號(hào)「程序員jinjunzhu」,作者jinjunzhu 。轉(zhuǎn)載本文請(qǐng)聯(lián)系程序員jinjunzhu公眾號(hào)。

分布式事務(wù)的實(shí)現(xiàn)方式中,TCC是比較知名的模式。但是我一直不喜歡這種模式,原因是這種模式有很多問(wèn)題要考慮。

之前寫(xiě)過(guò)一篇文章說(shuō)了TCC的很多缺點(diǎn),后來(lái)我把文章刪了,原因是一位阿里大佬加我好友并指正了我的觀點(diǎn)。

太感謝了!

1 TCC概要

簡(jiǎn)單來(lái)講,TCC模式就是將整個(gè)事務(wù)分成兩個(gè)階段來(lái)提交,try階段進(jìn)行預(yù)留資源,如果所有分支都預(yù)留成功,則進(jìn)入commit階段提交所有分支事務(wù),否則執(zhí)行cancel取消所有分支事務(wù)。

以電商系統(tǒng)為例,假如有訂單、庫(kù)存和賬戶(hù)3個(gè)服務(wù),客戶(hù)購(gòu)買(mǎi)一件商品,訂單服務(wù)增加訂單,庫(kù)存服務(wù)扣減庫(kù)存,賬戶(hù)服務(wù)扣減金額,這三個(gè)操作必須是原子性的,要么全部成功,要么全部失敗。

try階段

如下圖:

訂單服務(wù)增加一個(gè)訂單,庫(kù)存服務(wù)凍結(jié)訂單上的庫(kù)存,賬戶(hù)服務(wù)凍結(jié)訂單上的金額。

訂單、庫(kù)存和賬戶(hù)這三個(gè)服務(wù)作為整個(gè)分布式事務(wù)的分支事務(wù),在try階段都是要提交本地事務(wù)的。上面庫(kù)存和賬戶(hù)說(shuō)的凍結(jié),就是說(shuō)這個(gè)訂單對(duì)應(yīng)的庫(kù)存和金額已經(jīng)不能再被其他事務(wù)使用了,所以必須提交本地事務(wù)。

但這個(gè)提交并不是真正的提交全局事務(wù),而是把資源轉(zhuǎn)到中間態(tài),這個(gè)中間態(tài)需要在try方法的業(yè)務(wù)代碼中實(shí)現(xiàn),比如賬戶(hù)扣除的金額可以先存放到一個(gè)中間賬戶(hù)。

如果try階段不提交本地事務(wù)會(huì)有什么問(wèn)題呢?有可能其他事務(wù)在try階段發(fā)現(xiàn)用戶(hù)賬戶(hù)里面的金額還夠,但是commit的時(shí)候發(fā)現(xiàn)金額不夠了,commit階段扣款只能失敗,這時(shí)其他兩個(gè)分支事務(wù)提交成功而賬戶(hù)服務(wù)的分支事務(wù)提交失敗,最終數(shù)據(jù)就不一致了。

commit階段

如下圖:

commit階段,數(shù)據(jù)從中間態(tài)轉(zhuǎn)入終態(tài),比如訂單金額從中間賬戶(hù)轉(zhuǎn)到最終賬戶(hù)。

cancel階段跟commit階段類(lèi)似,比如訂單金額從中間賬戶(hù)退回到客戶(hù)賬戶(hù)。

2 問(wèn)題代碼

下面這段代碼也可以理解為T(mén)CC,是在try階段hold住了connection,不提交分支事務(wù),到commit階段再提交分支事務(wù)。代碼如下:我們以扣減賬戶(hù)為例,首先定義2個(gè)變量來(lái)hold住connection:

  
 
 
 
  1. private Map statementMap = new ConcurrentHashMap<>(100); 
  2. private Map connectionMap = new ConcurrentHashMap<>(100); 

try方法代碼如下:

  
 
 
 
  1. public boolean try(String xid, Long userId, BigDecimal payAmount) { 
  2.     LOGGER.info("decrease, xid:{}", xid); 
  3.     LOGGER.info("------->嘗試扣減賬戶(hù)開(kāi)始account"); 
  4.  
  5.     try { 
  6.         //嘗試扣減賬戶(hù)金額,事務(wù)不提交 
  7.         Connection connection = hikariDataSource.getConnection(); 
  8.         connection.setAutoCommit(false); 
  9.         String sql = "UPDATE account SET balance = balance - ?,used = used + ? where user_id = ?"; 
  10.         PreparedStatement stmt = connection.prepareStatement(sql); 
  11.         stmt.setBigDecimal(1, payAmount); 
  12.         stmt.setBigDecimal(2, payAmount); 
  13.         stmt.setLong(3, userId); 
  14.         stmt.executeUpdate(); 
  15.         statementMap.put(xid, stmt); 
  16.         connectionMap.put(xid, connection); 
  17.     } catch (Exception e) { 
  18.         LOGGER.error("decrease parepare failure:", e); 
  19.         return false; 
  20.     } 
  21.  
  22.     LOGGER.info("------->嘗試扣減賬戶(hù)結(jié)束account"); 
  23.  
  24.     return true; 

commit方法代碼如下:

  
 
 
 
  1. public boolean commit(BusinessActionContext actionContext){ 
  2.     String xid = actionContext.getXid(); 
  3.     PreparedStatement statement = (PreparedStatement) statementMap.get(xid); 
  4.     Connection connection = connectionMap.get(xid); 
  5.     try { 
  6.         if (null != connection){ 
  7.             connection.commit(); 
  8.         } 
  9.     } catch (SQLException e) { 
  10.         LOGGER.error("扣減賬戶(hù)失敗:", e); 
  11.         return false; 
  12.     }finally { 
  13.         try { 
  14.             statementMap.remove(xid); 
  15.             connectionMap.remove(xid); 
  16.             if (null != statement){ 
  17.                 statement.close(); 
  18.             } 
  19.             if (null != connection){ 
  20.                 connection.close(); 
  21.             } 
  22.         } catch (SQLException e) { 
  23.             LOGGER.error("扣減賬戶(hù)提交事務(wù)后關(guān)閉連接池失敗:", e); 
  24.         } 
  25.     } 
  26.     return true; 

cancel方法代碼如下:

  
 
 
 
  1. public boolean rollback(BusinessActionContext actionContext){ 
  2.     String xid = actionContext.getXid(); 
  3.     PreparedStatement statement = (PreparedStatement) statementMap.get(xid); 
  4.     Connection connection = connectionMap.get(xid); 
  5.     try { 
  6.         connection.rollback(); 
  7.     } catch (SQLException e) { 
  8.         return false; 
  9.     }finally { 
  10.         try { 
  11.             if (null != statement){ 
  12.                 statement.close(); 
  13.             } 
  14.             if (null != connection){ 
  15.                 connection.close(); 
  16.             } 
  17.             statementMap.remove(xid); 
  18.             connectionMap.remove(xid); 
  19.         } catch (SQLException e) { 
  20.             LOGGER.error("扣減賬戶(hù)回滾事務(wù)后關(guān)閉連接池失敗:", e); 
  21.         } 
  22.     } 
  23.     return true; 

這段代碼是問(wèn)題代碼,不能用,不能用,不能用

這個(gè)代碼存在兩個(gè)問(wèn)題:

2.1 阻塞等待

如果當(dāng)前事務(wù)不提交,比如賬戶(hù)服務(wù),那就相當(dāng)于是鎖定了資源,后面的事務(wù)只能等待資源釋放。

2.2 服務(wù)集群

以訂單服務(wù)為例,假如訂單服務(wù)是一個(gè)3個(gè)機(jī)器的集群,如下圖:

協(xié)調(diào)節(jié)點(diǎn)使用注冊(cè)中心客戶(hù)端來(lái)調(diào)用訂單服務(wù),如果try請(qǐng)求發(fā)送到了訂單服務(wù)1,而commit請(qǐng)求發(fā)送到了訂單服務(wù)2,那訂單服務(wù)2上的connectionMap里不會(huì)有xid=123這個(gè)connection,只能提交失敗。

3 TCC存在的問(wèn)題

上面的問(wèn)題代碼就是給大家一個(gè)思路,如果真要hold住connection,也算是實(shí)現(xiàn)了TCC的思想,但是在系統(tǒng)中,我們是不可能這樣做的,所以把它叫做問(wèn)題代碼。

3.1 空回滾

如下圖,訂單服務(wù)1節(jié)點(diǎn)故障,如果不考慮重試,try方法失?。?/p>

try雖然失敗了,但是全局事務(wù)已經(jīng)開(kāi)啟,框架必須要把這個(gè)全局事務(wù)推向結(jié)束狀態(tài),這就不得不調(diào)用訂單服務(wù)cancel方法進(jìn)行回滾,結(jié)果訂單服務(wù)空跑了一次cancel方法。

解決這個(gè)問(wèn)題,可以記錄一張事務(wù)控制表,保存全局事務(wù)xid和分支事務(wù)branchId,try階段會(huì)插入一條記錄,表示try階段執(zhí)行了。cancel方法讀取該記錄,如果記錄存在,正?;貪L;如果該記錄不存在,那就是空回滾。

3.2 冪等

冪等是指在commit/cancel階段,因?yàn)門(mén)C沒(méi)有收到分支事務(wù)的響應(yīng),需要進(jìn)行重試,這就要分支事務(wù)支持冪等。以訂單服務(wù)為例。如下圖:

要支持冪等,可以記錄一張事務(wù)控制表,保存全局事務(wù)xid和分支事務(wù)branchId,以及分支事務(wù)狀態(tài),在第二階段commit/cancel之前先檢查分支事務(wù)狀態(tài)是否已經(jīng)是終態(tài),如果不是,再執(zhí)行第二階段的邏輯。

3.3 懸掛

懸掛是指事務(wù)的cancel方法比try方法先執(zhí)行。上面講了seata的使用過(guò)程中會(huì)發(fā)生空回滾,如果發(fā)生了空回滾,執(zhí)行了cancel方法后全局事務(wù)結(jié)束了,但是因?yàn)榫W(wǎng)絡(luò)問(wèn)題,訂單服務(wù)又收到了try請(qǐng)求,執(zhí)行try方法后預(yù)留資源成功,這些資源最終不能釋放了。

解決這個(gè)問(wèn)題的方法就是在cancel方法中記錄xid對(duì)應(yīng)的分支事務(wù)回滾記錄,try階段執(zhí)行的時(shí)候先判斷分支事務(wù)是否已經(jīng)回滾,如果存在回滾記錄,則直接退出。

3.4 業(yè)務(wù)代碼侵入

TCC的try/commit/cancel,對(duì)業(yè)務(wù)代碼都有侵入,而且每個(gè)方法都是一個(gè)本地事務(wù)。再加上需要考慮冪等、空回滾、懸掛等,代碼侵入會(huì)更高。

4.TCC優(yōu)勢(shì)

這里以seata實(shí)現(xiàn)的四種模式來(lái)比較,包括XA、SAGA、TCC、AT。

效率

使用TCC模式時(shí),在try階段就提交了本地事務(wù),并不會(huì)鎖定資源,所以沒(méi)有其他額外的性能開(kāi)銷(xiāo)。相比之下,來(lái)看其他幾種模式:

  • AT模式,需要記錄undolog,性能損耗很大。
  • XA模式,執(zhí)行xa start | sql | xa end之后,執(zhí)行commit/rollback之前,會(huì)鎖定資源,后面的事務(wù)需要等待。

saga模式

更適合長(zhǎng)流程的業(yè)務(wù)場(chǎng)景。

5.性能優(yōu)化

參考[1]

5.1 異步提交

優(yōu)化思路是try階段成功后,不立即執(zhí)行confirm/cancel階段,而是等系統(tǒng)空閑的時(shí)候異步執(zhí)行。如下圖:

這樣在try階段結(jié)束后,就認(rèn)為全局事務(wù)結(jié)束了,可以定時(shí)(比如10分鐘)來(lái)異步執(zhí)行第二階段,性能大幅提升。

當(dāng)然,帶來(lái)的一點(diǎn)問(wèn)題就是如果全局事務(wù)回滾,會(huì)有短暫的數(shù)據(jù)不一致。比如扣款的場(chǎng)景,定時(shí)10分鐘執(zhí)行一次異步任務(wù),如果第二階段是cancel,那客戶(hù)會(huì)在這10分鐘內(nèi)不能使用這筆金額。

這個(gè)異步執(zhí)行的時(shí)間也可以根據(jù)業(yè)務(wù)來(lái)決定,比如不需要及時(shí)從中間賬戶(hù)轉(zhuǎn)移到最終賬戶(hù)的場(chǎng)景可以設(shè)置更長(zhǎng)。

5.2 同庫(kù)模式

首先回顧一下TCC中各個(gè)角色:

  • TM管理全局事務(wù),包括開(kāi)啟全局事務(wù),提交/回滾全局事務(wù)
  • RM管理分支事務(wù)
  • TC管理全局事務(wù)和分支事務(wù)的狀態(tài)

先看一下優(yōu)化之前的通信模型,如下圖:

在優(yōu)化之前,TM開(kāi)啟全局事務(wù)時(shí),RM需要向TC發(fā)送RPC消息進(jìn)行注冊(cè),TC保存分支事務(wù)的狀態(tài)。TM請(qǐng)求提交或回滾時(shí),TC需要向RM發(fā)送RPC消息進(jìn)行提交或回滾。這樣包含兩個(gè)個(gè)分支事務(wù)的分布式事務(wù)中,TC和RM之間有四次RPC。

優(yōu)化之后的模型如下圖:

TM開(kāi)啟全局事務(wù)時(shí),不再需要向TC注冊(cè)分支事務(wù),而是把分支事務(wù)狀態(tài)保存在了本地。TM向TC發(fā)送提交或回滾消息時(shí),TC保存全局事務(wù)的狀態(tài)。而RM則啟動(dòng)異步線(xiàn)程檢測(cè)本地記錄的未提交分支事務(wù),向TC發(fā)送RPC消息獲取整體事務(wù)狀態(tài),以決定是提交還是回滾本地事務(wù)。可見(jiàn),優(yōu)化后的模型,RPC次數(shù)減少了50%,性能大幅提升。

6.總結(jié)

TCC的問(wèn)題確實(shí)不少,但是除了侵入業(yè)務(wù)代碼這一個(gè)問(wèn)題,其他問(wèn)題都有對(duì)應(yīng)的解決方案。

阿里針對(duì)TCC做了一些優(yōu)化,包括第二階段異步提交和同庫(kù)模式,性能提升很明顯。


當(dāng)前文章:分布式事務(wù),阿里為什么鐘愛(ài)TCC
鏈接URL:http://www.5511xx.com/article/cdepjdc.html