江湖論道:分布式事務(wù)
什么是事務(wù)
指訪問(wèn)并可能更新數(shù)據(jù)庫(kù)中各種數(shù)據(jù)項(xiàng)的一個(gè)程序執(zhí)行單元。組成事務(wù)的所有操作,只有在都執(zhí)行成功的情況下才能提交,只要有一個(gè)操作執(zhí)行失敗,都將導(dǎo)致事務(wù)回滾。
本地事務(wù)
在計(jì)算機(jī)系統(tǒng)中,通過(guò)關(guān)系型數(shù)據(jù)庫(kù)來(lái)控制事務(wù),單體應(yīng)用中數(shù)據(jù)庫(kù)通常和應(yīng)用在同一服務(wù)器上,因此成為本地事務(wù)。
數(shù)據(jù)庫(kù)事務(wù)的四大特性:
原子性(Atomicity) :事務(wù)包含的所有操作要么全部成功,要么全部失敗回滾。 一致性(Consistency) :一致性是指事務(wù)必須使數(shù)據(jù)庫(kù)從一個(gè)一致性狀態(tài)變換到另一個(gè)一致性狀態(tài)。 隔離性(Isolation) :一個(gè)事務(wù)不能被其他事務(wù)的操作所干擾,多個(gè)并發(fā)事務(wù)之間要相互隔離。 持久性(Durability) :事務(wù)一旦提交,對(duì)數(shù)據(jù)庫(kù)中數(shù)據(jù)的改變就應(yīng)該是永久性的。
分布式事務(wù)
分布式事務(wù)就是由多個(gè)本地事務(wù)組合而成的事務(wù),一般在分布式場(chǎng)景下才會(huì)出現(xiàn)。分布式事務(wù)用于解決分布式環(huán)境下,跨系統(tǒng)應(yīng)用的一致性問(wèn)題。
分布式事務(wù)基礎(chǔ)理論
CAP
數(shù)據(jù)一致性(consistency):一致性指的是所有節(jié)點(diǎn)在同一時(shí)間的數(shù)據(jù)完全一致。如果系統(tǒng)對(duì)一個(gè)寫操作返回成功,那么之后的讀請(qǐng)求都必須讀到這個(gè)新數(shù)據(jù);如果返回失敗,那么之后的讀操作都不能讀到這個(gè)數(shù)據(jù),對(duì)調(diào)用者而言,數(shù)據(jù)具有強(qiáng)一致性。 服務(wù)可用性(availability): 只要收到用戶的請(qǐng)求,服務(wù)器就必須給出回應(yīng)。 分區(qū)容錯(cuò)性(partition-tolerance): 在網(wǎng)絡(luò)分區(qū)的情況下,被分隔的節(jié)點(diǎn)仍能正常對(duì)外服務(wù)。一般來(lái)說(shuō),分區(qū)容錯(cuò)無(wú)法避免,因此可以認(rèn)為 CAP 的 P 總是成立。CAP 定理告訴我們,剩下的 C 和 A 無(wú)法同時(shí)做到。
CAP無(wú)法同時(shí)滿足 如果CAP三者同時(shí)滿足,由于允許P的存在,則一定存在節(jié)點(diǎn)之間丟包的情況,而不能滿足C。因此,P是在分布式系統(tǒng)中,無(wú)法避免的,在分布式系統(tǒng)中進(jìn)行寫操作,如果滿足一致性,則會(huì)出現(xiàn)所有節(jié)點(diǎn)都失敗的情況,而不滿足可用性。因此,分布式系統(tǒng)只能選擇CP或者AP。
BASE
CAP理論告訴我們分布式系統(tǒng)中C(一致性),A(可用性)和P(分區(qū)容錯(cuò)性),最多滿足兩項(xiàng)。
BASE 是指基本可用(Basically Available)、軟狀態(tài)( Soft State)、最終一致性( Eventual Consistency),核心思想是即使無(wú)法做到強(qiáng)一致性(CAP 的一致性就是強(qiáng)一致性),但應(yīng)用可以采用適合的方式達(dá)到最終一致性。
BA (Basically Available) 基本可用 分布式系統(tǒng)在出現(xiàn)故障時(shí),允許損失部分可用性,即保證核心可用。 S (Soft State )軟狀態(tài) 允許系統(tǒng)存在中間狀態(tài)(軟狀態(tài)),而該中間狀態(tài)不會(huì)影響系統(tǒng)整體可用性。如“創(chuàng)建中”,“正在同步”等狀態(tài),等到數(shù)據(jù)最終一致后,改為“成功”的狀態(tài)。這里的中間狀態(tài)就是 CAP 理論中的數(shù)據(jù)不一致。 E (Eventual Consistency) 最終一致性 系統(tǒng)中的所有節(jié)點(diǎn)數(shù)據(jù)經(jīng)過(guò)一定時(shí)間后,最終能夠達(dá)到一致的狀態(tài)。比如訂單的"支付中",最終會(huì)變成"支付成功"或者"支付失敗"。
BASE 理論本質(zhì)上是對(duì) CAP 的延伸和補(bǔ)充,更具體地說(shuō),是對(duì) CAP 中 AP 方案的一個(gè)擴(kuò)展,通過(guò)犧牲強(qiáng)一致性來(lái)獲得可用性。當(dāng)出現(xiàn)故障時(shí),允許部分不可用,要保證核心功能可用,允許數(shù)據(jù)在一段時(shí)間內(nèi)不一致,但最終達(dá)到一致。
分布式事務(wù)解決方案
XA模式
XA是由Tuxedo提出的一個(gè)分布式事務(wù)協(xié)議。大致分為兩部分:事務(wù)管理器和本地資源管理器。其中本地資源管理器往往由數(shù)據(jù)庫(kù)實(shí)現(xiàn),比如Oracle、DB2這些商業(yè)數(shù)據(jù)庫(kù)都實(shí)現(xiàn)了XA接口,而事務(wù)管理器作為全局的調(diào)度者,負(fù)責(zé)各個(gè)本地資源的提交和回滾。
2PC(Two-phase Commit兩階段提交)
在二階段提交中有兩個(gè)角色:
事務(wù)協(xié)調(diào)者:分布式事務(wù)的大腦,負(fù)責(zé)指揮協(xié)調(diào)各個(gè)業(yè)務(wù)系統(tǒng)提交/回滾事務(wù) 事務(wù)參與者:本地資源管理器,即事務(wù)的執(zhí)行者,也就是各個(gè)業(yè)務(wù)系統(tǒng)。
兩階段提交協(xié)議的具體步驟:
準(zhǔn)備階段(Prepare phase): 事務(wù)管理器(TM)給每個(gè)參與者發(fā)送Prepare消息,每個(gè)數(shù)據(jù)庫(kù)參與者在本地執(zhí)行事務(wù),此時(shí)事務(wù)沒(méi)有提交。記錄日志(undo log 記錄修改前的數(shù)據(jù),用于數(shù)據(jù)回滾/ redo log記錄修改后的數(shù)據(jù),用于事務(wù)提交后寫入數(shù)據(jù)文件)
提交階段(Commit phase): 如果事務(wù)管理器(TM)收到了參數(shù)者的執(zhí)行失敗或超時(shí)的消息時(shí),直接給每個(gè)參與者發(fā)送回滾(Rollback) 消息;否則,發(fā)送提交消息(commit); 參與者根據(jù)事務(wù)管理器的指令執(zhí)行提交或回滾。并釋放事務(wù)處理中使用的鎖資源。

XA兩階段提交究竟有哪些不足呢?
協(xié)調(diào)者單點(diǎn)故障問(wèn)題
事務(wù)協(xié)調(diào)者是整個(gè)XA模型的核心,一旦事務(wù)協(xié)調(diào)者節(jié)點(diǎn)掛掉,參與者收不到提交或是回滾通知,參與者會(huì)一直阻塞下去,處于中間狀態(tài)無(wú)法完成事務(wù)。
丟失消息導(dǎo)致的不一致問(wèn)題。
協(xié)調(diào)者發(fā)出請(qǐng)求可能由于網(wǎng)絡(luò)原因得不到響應(yīng)。在XA協(xié)議的第二個(gè)階段,如果發(fā)生局部網(wǎng)絡(luò)問(wèn)題,一部分事務(wù)參與者收到了提交消息,另一部分事務(wù)參與者沒(méi)收到提交消息,那么就導(dǎo)致了節(jié)點(diǎn)之間數(shù)據(jù)的不一致。
同步阻塞
資源鎖需要等待兩階段結(jié)束后才能釋放,性能較差。
3PC(三階段提交)
把兩階段提交的準(zhǔn)備階段一分為二,加入了一個(gè)預(yù)提交的緩沖階段。同時(shí)在協(xié)調(diào)者和參與者中引入超時(shí)機(jī)制。
三階段提交將二階段的準(zhǔn)備階段拆分為兩個(gè)階段 。
三階段提交流程
階段一:canCommit 協(xié)調(diào)者向參與者發(fā)送commit請(qǐng)求, 參與者如果可以提交,就返回yes,并進(jìn)入預(yù)備狀態(tài),否則返回no。參與者不執(zhí)行事務(wù)。
階段二:preCommit 協(xié)調(diào)者根據(jù)上階段 canCommit參與者的反應(yīng)來(lái)確定是否可以基于事務(wù)的preCommit操作。如果一階段所以參與者都返回yes,參與者預(yù)執(zhí)行事務(wù),如果有任意一個(gè)參與者反饋no,或者等待超時(shí),協(xié)調(diào)者無(wú)法收到參與者的反饋,就中斷事務(wù)。
階段三:doCommit 與2PC的提交階段差不多。
優(yōu)點(diǎn):相比較2PC而言,3PC對(duì)于協(xié)調(diào)者和參與者都設(shè)置了超時(shí)時(shí)間,避免了參與者在長(zhǎng)時(shí)間無(wú)法與協(xié)調(diào)者節(jié)點(diǎn)通訊(協(xié)調(diào)者掛掉了)的情況下,無(wú)法釋放資源的問(wèn)題,因?yàn)閰⑴c者自身?yè)碛谐瑫r(shí)機(jī)制會(huì)在超時(shí)后,自動(dòng)進(jìn)行本地commit從而進(jìn)行釋放資源。而這種機(jī)制也側(cè)面降低了整個(gè)事務(wù)的阻塞時(shí)間和范圍。
缺點(diǎn):依然不能解決數(shù)據(jù)一致性問(wèn)題。
TCC模式
理論
TCC又稱補(bǔ)償事務(wù)。實(shí)際上是服務(wù)化的兩階段提交協(xié)議,開(kāi)發(fā)者需要編碼實(shí)現(xiàn) Try、Confirm、Cancel這三個(gè)服務(wù)接口。其核心思想是:"針對(duì)每個(gè)操作都要注冊(cè)一個(gè)與其對(duì)應(yīng)的確認(rèn)和補(bǔ)償(撤銷操作)"
TCC對(duì)應(yīng) Try、Confirm、Cancel 三種操作
預(yù)處理 Try : 主要是做業(yè)務(wù)檢查和資源預(yù)留
確認(rèn) Confirm: 確認(rèn)提交,Try階段所有的分支事務(wù)執(zhí)行成功后,開(kāi)始執(zhí)行Confirm, 和Commit類似。
Cancel階段:Cancel則是當(dāng)Try操作中涉及的所有應(yīng)用沒(méi)有全部成功,需要將已成功的應(yīng)用進(jìn)行Rollback回滾

TCC需要注意的三種異常處理
空回滾
Q: 分支事務(wù)所在的服務(wù)器宕機(jī)或者網(wǎng)絡(luò)異常,此時(shí)沒(méi)有執(zhí)行Try, 當(dāng)故障恢復(fù)后,會(huì)調(diào)用Cancel接口進(jìn)行回滾。那么,
如何判斷是空回滾還是正?;貪L?
A: 增加一張分支事務(wù)表,其中包含全局事務(wù)ID和分支事務(wù)ID,在第一階段Try方法里插入一條記錄,表示一階段執(zhí)行了,Cancel階段接口執(zhí)行讀取該記錄,如果存在記錄,則正?;貪L,不存在則空回滾。
懸掛
Q: 通過(guò)RPC調(diào)用Try方法時(shí),如果發(fā)生網(wǎng)絡(luò)擁堵,通常RPC調(diào)用有超時(shí)機(jī)制的,超時(shí)后,TM就會(huì)通知RM回滾資源,回滾完成后,Try方法才執(zhí)行,鎖住了資源,無(wú)法釋放。
Cancel發(fā)生在Try之前,業(yè)務(wù)資源預(yù)留了以后,沒(méi)辦法處理怎么辦?
A: 分支事務(wù)記錄表中如果記錄了Cancel階段已經(jīng)執(zhí)行過(guò),則Try方法不再執(zhí)行。
冪等
Q: Confirm和Cancel執(zhí)行失敗后需要進(jìn)行重試,如何保證冪等?
A: 分支事務(wù)記錄表中維護(hù)執(zhí)行狀態(tài),每次執(zhí)行前都查詢一下該狀態(tài)。
事務(wù)管理器(TM)可以實(shí)現(xiàn)為獨(dú)立的服務(wù),也可讓全局事務(wù)發(fā)起方作為事務(wù)管理器。TM在發(fā)起全局事務(wù)時(shí)生成全局事務(wù)記錄,全局事務(wù)ID貫穿整個(gè)分布式事務(wù)調(diào)用鏈路,用來(lái)記錄事務(wù)上下文,追蹤和記錄狀態(tài),由于Confirm和Cancel失敗都要進(jìn)行重試,因此要實(shí)現(xiàn)冪等。
TCC與XA比較
XA是資源層面的分布式事務(wù),TCC業(yè)務(wù)層面的分布式事務(wù)。
Saga
Saga模型又稱長(zhǎng)時(shí)間運(yùn)行的事務(wù)(Long-running-transaction), 它是由普林斯頓大學(xué)的H.Garcia-Molina等人提出。Saga模型將分布式事務(wù)分為多個(gè)本地事務(wù),每個(gè)本地事務(wù)都有相應(yīng)的執(zhí)行模塊和補(bǔ)償模塊,任何一個(gè)本地事務(wù)出錯(cuò)時(shí),都可以通過(guò)調(diào)用相關(guān)的補(bǔ)充方法實(shí)現(xiàn)事務(wù)的最終一致性。

Saga適合于長(zhǎng)事務(wù)的應(yīng)用場(chǎng)景。
缺點(diǎn):由于Saga模型沒(méi)有準(zhǔn)備階段,因此沒(méi)有實(shí)現(xiàn)事務(wù)隔離。如果兩個(gè)事務(wù)同時(shí)操作同一資源則會(huì)產(chǎn)生并發(fā)讀寫問(wèn)題,這時(shí)就需要在應(yīng)用層面加入資源鎖定的邏輯了。
可靠消息最終一致性
什么是可靠消息最終一致性
事務(wù)發(fā)起者執(zhí)行本地事務(wù)后,發(fā)出一條消息,事務(wù)參與者(消息消費(fèi)者)一定能接受到消息并處理成功。
此方案利用消息中間件完成,事務(wù)發(fā)起方(消息生產(chǎn)者)將消息發(fā)送給消息中間件,事務(wù)參與方從消息中間件接受消息,事務(wù)發(fā)起方和消息中間件之間,事務(wù)參與者和消息中間件之間都是通過(guò)網(wǎng)絡(luò)通信。所以會(huì)因?yàn)榫W(wǎng)絡(luò)問(wèn)題存在分布式事務(wù)的問(wèn)題。
需要解決的問(wèn)題
本地事務(wù)與消息發(fā)送的原子性問(wèn)題
本地事務(wù)和發(fā)送消息,要么都成功,要么都失敗。
如果先發(fā)送消息,再操作數(shù)據(jù)庫(kù)。無(wú)法保證數(shù)據(jù)庫(kù)操作和發(fā)送消息的原子性,可能發(fā)送消息成功,但是數(shù)據(jù)庫(kù)操作失敗。 先操作數(shù)據(jù)庫(kù),再發(fā)送消息。如果消息發(fā)送失敗,拋出異常,從而數(shù)據(jù)庫(kù)回滾。看似沒(méi)問(wèn)題,但是如果超時(shí)異常,數(shù)據(jù)庫(kù)回滾,但是消息其實(shí)已經(jīng)發(fā)出了,同樣會(huì)有上述問(wèn)題。 事務(wù)參與者接收消息的可靠性
事務(wù)參與者必須能從消息隊(duì)列接收到消息,如果接收消息失敗可以重復(fù)接收。
消息重復(fù)消費(fèi)
要解決消費(fèi)重復(fù)的問(wèn)題,要實(shí)現(xiàn)事務(wù)參與者的方法冪等性。
可靠消息最終一致性實(shí)現(xiàn)方案
本地消息表
消息方式放在信息入庫(kù)的同時(shí),維護(hù)一張消息記錄表,添加一條消息記錄到數(shù)據(jù)庫(kù)表,通過(guò)本地事務(wù)保證業(yè)務(wù)操作和消息一致性。然后通過(guò)定時(shí)任務(wù)將消息發(fā)送到消息中間件,等待確認(rèn)消息發(fā)送給消費(fèi)方時(shí),將消息表中數(shù)據(jù)刪除。
缺點(diǎn):定時(shí)任務(wù)輪詢掃描數(shù)據(jù)庫(kù)消息表,會(huì)影響數(shù)據(jù)庫(kù)性能。
RocketMQ事務(wù)消息

RocketMQ事務(wù)消息發(fā)送步驟:
發(fā)送方將半事務(wù)消息發(fā)送至消息隊(duì)列RocketMQ版服務(wù)端。半事務(wù)消息不會(huì)被訂閱方消費(fèi)。 MQ Server 將消息持久化成功之后,向發(fā)送方返回Ack確認(rèn)消息已經(jīng)發(fā)送成功,此時(shí)消息為半事務(wù)消息。 發(fā)送方開(kāi)始執(zhí)行本地事務(wù)邏輯。 發(fā)送方根據(jù)本地事務(wù)執(zhí)行結(jié)果向MQ 服務(wù)端提交二次確認(rèn)(Commit或是Rollback),服務(wù)端收到Commit狀態(tài)則將半事務(wù)消息標(biāo)記為可投遞,訂閱方最終將收到該消息;服務(wù)端收到Rollback狀態(tài)則刪除半事務(wù)消息,訂閱方將不會(huì)接受該消息。
Q: 在斷網(wǎng)或者是應(yīng)用重啟的特殊情況下,上述步驟4提交的二次確認(rèn)最終未到達(dá)服務(wù)端怎么辦?
RocketMQ服務(wù)端會(huì)對(duì)該消息發(fā)起消息進(jìn)行回查。發(fā)送方收到消息回查后,需要檢查對(duì)應(yīng)消息的本地事務(wù)執(zhí)行的最終結(jié)果, 發(fā)送方根據(jù)檢查得到本地事務(wù)的最終狀態(tài)再次提交二次確認(rèn),服務(wù)器再按照步驟4對(duì)半事務(wù)消息進(jìn)行操作。
最大努力通知
當(dāng)本地事務(wù)執(zhí)行完成后發(fā)送消息到MQ,MQ通知另一個(gè)系統(tǒng)執(zhí)行事務(wù),如果失敗了則重試,多次嘗試后還是不成功則放棄。允許少數(shù)事務(wù)失敗,一般用在對(duì)分布式事務(wù)要求不嚴(yán)格的情況下,比如記錄日志或者狀態(tài)等。
Seata
Seata是阿里中間件團(tuán)隊(duì)發(fā)起的開(kāi)源項(xiàng)目Fescar,后更名Seata,是一個(gè)開(kāi)源分布式事務(wù)框架。
Seata 中有三大模塊,分別是 TM、RM 和 TC。

Transaction Coordinator (TC) : 事務(wù)協(xié)調(diào)器,它是獨(dú)立的中間件,需要獨(dú)立部署運(yùn)行。它維護(hù)全局事務(wù)的運(yùn)行狀態(tài),接收TM的指令發(fā)起全局事務(wù)的提交或回滾。負(fù)責(zé)與RM通信協(xié)調(diào)各個(gè)分支事務(wù)的提交或回滾。Transaction Manager (TM) : 事務(wù)管理器, TM是嵌入應(yīng)用中工作,負(fù)責(zé)開(kāi)啟一個(gè)全局事務(wù)并在最終向TC發(fā)起全局提交或全局回滾的指令。 Resource Manager (RM) : 控制分支事務(wù),負(fù)責(zé)分支注冊(cè),狀態(tài)匯報(bào),并接受事務(wù)協(xié)調(diào)器TC的指令,驅(qū)動(dòng)分支事務(wù)的提交和回滾。
Seata執(zhí)行流程

TM要求TC開(kāi)始一個(gè)新的全局事務(wù)。TC生成一個(gè)表示全局事務(wù)的XID。XID通過(guò)微服務(wù)的調(diào)用鏈傳播。
RM將本地事務(wù)作為對(duì)應(yīng)的XID全局事務(wù)的分支注冊(cè)到TC。
TM請(qǐng)求TC提交或回滾相應(yīng)的XID全局事務(wù)。
TC驅(qū)動(dòng)XID對(duì)應(yīng)全局事務(wù)下的所有分支事務(wù),以完成分支提交或回滾。
Seata 提供了 4 種分布式事務(wù)解決方案, AT 模式、TCC 模式、Saga 模式和 XA 模式。
AT模式是Seata實(shí)現(xiàn)的2PC。解決了XA模式的2PC需要依賴數(shù)據(jù)庫(kù)支持XA協(xié)議的弊端,又對(duì)業(yè)務(wù)零侵入。
在 AT 模式下,開(kāi)發(fā)者可以只用關(guān)心自己的業(yè)務(wù) SQL,將業(yè)務(wù) SQL的執(zhí)行作為一階段,Seata 會(huì)自動(dòng)生成事務(wù)的二階段提交或回滾。
寫在最后
歡迎多多批評(píng)更正。
- END -點(diǎn)個(gè)在看你最好看
