分布式事務(wù)理論加實(shí)戰(zhàn)
點(diǎn)擊上方藍(lán)色字體,選擇“標(biāo)星公眾號”
優(yōu)質(zhì)文章,第一時(shí)間送達(dá)
為什么需要分布式事務(wù)
隨著互聯(lián)網(wǎng)的快速發(fā)展,業(yè)務(wù)越來越復(fù)雜,一個(gè)完整的業(yè)務(wù)往往需要調(diào)用多個(gè)子服務(wù),涉及的數(shù)據(jù)也越來越多。傳統(tǒng)的系統(tǒng)難以支撐,就出現(xiàn)了分布式系統(tǒng),而分布式系統(tǒng)又帶來了數(shù)據(jù)一致性的問題,從而產(chǎn)生了分布式事務(wù)。

什么叫分布式事務(wù)
分布式條件下,多個(gè)節(jié)點(diǎn)操作的整體事務(wù)一致性。
特別是在微服務(wù)場景下,業(yè)務(wù)A和業(yè)務(wù)B關(guān)聯(lián),如果事務(wù)A成功了,事務(wù)B失敗了。由于跨系統(tǒng),事務(wù)B無法通知到事務(wù)A,就造成了數(shù)據(jù)的不一致。
如何實(shí)現(xiàn)分布式下的一致性
強(qiáng)一致性
XA
弱一致性
不用事務(wù),業(yè)務(wù)側(cè)補(bǔ)償沖正
柔性事務(wù),使用一套事務(wù)框架保證最終一致性的事務(wù)。
一、強(qiáng)一致性事務(wù)
1. XA分布式事務(wù)
在學(xué)習(xí)XA之前,我們先了解一下DTP模型,該模型規(guī)范了分布式事務(wù)的模型設(shè)計(jì)

應(yīng)用程序(Application Program):定義事務(wù)邊界(即事務(wù)的開始和結(jié)束),并且在事務(wù)邊界內(nèi)對資源進(jìn)行操作
資源管理器(Resource Manager):如數(shù)據(jù)庫、文件系統(tǒng)等。并提供訪問資源的方式
事務(wù)管理器(Transaction Manager):負(fù)責(zé)分配事務(wù)唯一標(biāo)識,監(jiān)控事務(wù)的執(zhí)行進(jìn)度,并負(fù)責(zé)事務(wù)的提交、回滾等。
XA協(xié)議 是由 X/Open 組織提出的,作為資源管理器與事務(wù)管理器的接口標(biāo)準(zhǔn).目前Oracle、DB2、MySQL的InnoDB存儲(chǔ)引擎都對XA進(jìn)行了支持。XA接口提供資源管理器與事務(wù)管理器之間進(jìn)行通信的標(biāo)準(zhǔn)接口。XA協(xié)議包括兩套函數(shù),以xa_開頭的及以ax_開頭的。

xa_start:負(fù)責(zé)開啟或者恢復(fù)一個(gè)事務(wù)分支
xa_end:負(fù)責(zé)取消當(dāng)前線程和事務(wù)分支的關(guān)聯(lián)
xa_prepare:詢問RM是否準(zhǔn)備好提交事務(wù)分支
xa_commit:通知RM提交事務(wù)分支
xa_rollback:通知RM回滾事務(wù)分支
xa_recover:列出所有prepare的XA事務(wù)
MySQL 從5.0.3開始支持 InnoDB 引擎的 XA 分布式事務(wù),MySQL Connector/J 從5.0.0版本開始支持 XA
MySQL XA事務(wù)狀態(tài)

完整的XA事務(wù)流程
XA事務(wù)異常情況
業(yè)務(wù)SQL執(zhí)行期間,某個(gè)RM崩潰怎么處理?
答:通知回滾。
2. 全部prepare后,某個(gè)RM崩潰怎么處理?
答:5.7以前崩潰的那個(gè)RM會(huì)丟失事務(wù),導(dǎo)致別人都提交了,他被回滾了。5.7之后修復(fù)了,重連后還能繼續(xù)提交。
commit時(shí),某個(gè)RM崩潰了怎么辦?
答:RM恢復(fù)之后重試,要是重試還是失敗就要發(fā)送告警,人工進(jìn)行干預(yù)。
XA協(xié)議存在的問題
同步阻塞問題
全局事務(wù)內(nèi)部包含了多個(gè)獨(dú)立的事務(wù)分支,這一組事務(wù)分支,這一組事務(wù)分支要不都成功,要不都失敗。各個(gè)事務(wù)分支的ACID特性構(gòu)成了全局事務(wù)的ACID特性。那么mysql的效率也會(huì)降低
2. 單點(diǎn)故障
TM是單點(diǎn)的,一旦TM發(fā)生故障,參與者RM會(huì)一直阻塞下去。尤其再第二階段,TM發(fā)生故障,那么所有的RM都還處于鎖定資源的狀態(tài)中,而無法完成事務(wù)操作。成熟的XA框架需要考慮TM的高可用性。
數(shù)據(jù)不一致
在提交階段的時(shí)候,TM向RM發(fā)送commit請求后,發(fā)生了局部網(wǎng)絡(luò)異常或者在發(fā)送commit請求的時(shí)候TM故障了,會(huì)導(dǎo)致部分RM收到commit請求并執(zhí)行,而部分RM未收到commit請求則無法進(jìn)行事務(wù)提交,就會(huì)造成數(shù)據(jù)不一致的情況。
支持XA的框架
XA方面的框架,比較推薦Atomikos和narayana
二、柔性事務(wù)
如果將實(shí)現(xiàn)了ACID的事務(wù)要素的事務(wù)稱為剛性事務(wù)的話,那么基于BASE理論的事務(wù)則稱為柔性事務(wù)。
BASE:
Basically Available (基本可用)
Soft state(柔性狀態(tài)) 允許系統(tǒng)狀態(tài)更新有一定的延時(shí)
Eventually consistent(最終一致性)
柔性事務(wù)常見模式
1. TCC
TCC模式將每個(gè)服務(wù)的業(yè)務(wù)操作分為兩個(gè)階段。第一個(gè)階段檢查并預(yù)留相關(guān)資源(Try),第二個(gè)階段根據(jù)Try狀態(tài),如果都成功,則進(jìn)行Comfirm操作,如果任意一個(gè)發(fā)生錯(cuò)誤,則全部Cancel。
Try:完成所有業(yè)務(wù)檢查,預(yù)留資源
Confirm:正在執(zhí)行的業(yè)務(wù)邏輯,不做業(yè)務(wù)檢查,只是要Try階段預(yù)留的業(yè)務(wù)資源。因此,只要Try成功,Confirm基本能成功。另外Confirm需要滿足冪等性。
Cancel:釋放Try階段的資源。同樣Cancel也需要滿足冪等性。
TCC不依賴RM對分布式事務(wù)的支持,而是通過對業(yè)務(wù)邏輯的分解來實(shí)現(xiàn)分布式事務(wù)。對業(yè)務(wù)有侵入性

TCC需要注意的問題:
允許空回滾
Cancel的時(shí)候要判斷Try有沒有完成,沒完成就不做Cancel
2. 防懸掛控制
如果網(wǎng)絡(luò)等數(shù)據(jù)庫還沒收到Try的執(zhí)行命令,Cancel命令先收到了。就會(huì)導(dǎo)致這個(gè)Try命令就沒有相對于的Cancel操作了,會(huì)一直懸掛在那里。
解決方法:
可以控制Try和Cancel的順序,讓Try在前面
先收到Cancel的時(shí)候,記錄一下。再收到Try的時(shí)候就知道這個(gè)操作是要取消的,那Try就沒必要執(zhí)行了。
冪等設(shè)計(jì)
commit操作可能會(huì)被重試,所以需要冪等性。
2. SAGA
Saga模式?jīng)]有Try階段,直接提交事務(wù)。復(fù)雜情況下,對回滾操作的設(shè)計(jì)要求較高。

3. AT
AT就是通過自動(dòng)生成反向SQL的方式進(jìn)行回滾。
在第一階段的時(shí)候執(zhí)行業(yè)務(wù)SQL,并且將SQL造成的影響保留下來。
第二階段如果發(fā)生異常,就會(huì)通過保留的影響用反向SQL恢復(fù)回去。
缺點(diǎn):生成反向SQL如果是在特別復(fù)雜的情況下,可能會(huì)處理不了。
4. 可靠消息最終一致性
服務(wù)A發(fā)送一個(gè)prepared消息給mq,如果發(fā)送失敗則取消操作
消息發(fā)送成功則開始執(zhí)行本地事務(wù)
本地事務(wù)執(zhí)行成功就向mq發(fā)送確認(rèn)消息,執(zhí)行失敗就發(fā)送回滾消息
如果mq沒有接收到確認(rèn)消息,mq會(huì)去輪詢未確認(rèn)的prepared消息,然后去查詢服務(wù)A是否執(zhí)行成功,然后確定是重試還是回滾
如果mq成功收到確認(rèn)消息,那么他會(huì)被服務(wù)B消費(fèi)到,并且服務(wù)B可以通過ACK機(jī)制保證服務(wù)B執(zhí)行成功.
如果服務(wù)B實(shí)在是無法執(zhí)行成功,可以通知服務(wù)A回滾,或者發(fā)送報(bào)警消息讓手工補(bǔ)償.

5. 本地消息表
服務(wù)A執(zhí)行業(yè)務(wù)代碼,并往自己的消息表插入一條數(shù)據(jù)
服務(wù)A執(zhí)行成功后,會(huì)向MQ發(fā)送一條數(shù)據(jù),去調(diào)用服務(wù)B的方法
服務(wù)B收到后,先往自己的消息表插入一條數(shù)據(jù),然后去執(zhí)行業(yè)務(wù)代碼
如果服務(wù)B業(yè)務(wù)代碼執(zhí)行成功,那么更新自己的消息表的狀態(tài)并且通知服務(wù)A更新消息表狀態(tài)
如果服務(wù)B業(yè)務(wù)代碼執(zhí)行失敗,那么服務(wù)B不用做什么
服務(wù)A會(huì)有一個(gè)定時(shí)任務(wù)定時(shí)輪詢自己的消息表,將失敗的消息再發(fā)給MQ,讓服務(wù)B重新在執(zhí)行一次(服務(wù)B保證接口冪等性)
通過不斷的重試,保證最終一致性
這個(gè)方案大量使用來消息表,對于高并發(fā)的場景不太友好.
6. 最大努力型通知
類似銀行的支付回調(diào),會(huì)多次回調(diào)直到成功。
這種方案適用于允許有些事務(wù)失敗的情況,如記錄日志等.
三、分布式事務(wù)框架
1. Seata
Seata是阿里巴巴和螞蟻金服聯(lián)合打造的分布式事務(wù)框架。其AT事務(wù)的目標(biāo)是讓開發(fā)者像使用本地事務(wù)一樣使用分布式事務(wù)。
核心組件:
TM 事務(wù)管理者:開啟提交或回滾全局事務(wù)
TC 事務(wù)協(xié)調(diào)者:維護(hù)全局和分支事務(wù)的狀態(tài),指示全局提交或回滾。
RM 資源管理者:管理執(zhí)行分支事務(wù)上的資源,向TC注冊分支事務(wù)、上報(bào)分支事務(wù)狀態(tài)、控制分支事務(wù)的提交或回滾。
Seata管理的分布式事務(wù)的典型聲明周期:
TM要求TC開始一個(gè)全新的全局事務(wù)。
TC生成一個(gè)代表該全局事務(wù)的XID。XID貫穿整個(gè)微服務(wù)的調(diào)用鏈。
TM要求TC提交或回滾XID對應(yīng)的全局事務(wù)。
TC驅(qū)動(dòng)XID對應(yīng)的全局事務(wù)下的所有分支事務(wù)完成提交或回滾。
Seata支持 XA、TCC、Saga模式,但支持的主要方式是 AT。
2. ShardingSphere對分布式事務(wù)的支持
ShardingSphere 通過整合常用的幾個(gè)事務(wù)開源實(shí)現(xiàn),如Atomkkos、Narayana,為本地事務(wù)、兩階段事務(wù)和柔性事務(wù)提供統(tǒng)一的分布式事務(wù)接口,并彌補(bǔ)當(dāng)前方案的不足,提供一站式的分布式事務(wù)解決方案是ShardingSphere的設(shè)計(jì)目標(biāo)。
使用實(shí)例:https://gitee.com/mmcLine/spring-cloud-transaction
里面readme有詳細(xì)的項(xiàng)目介紹。
作者 | 女友在高考
來源 | cnblogs.com/javammc/p/15098694.html

