分布式事務(wù)如何實(shí)現(xiàn)?深入解讀 Seata 的 XA 模式
Seata 1.2.0 版本重磅發(fā)布新的事務(wù)模式:XA 模式,實(shí)現(xiàn)對(duì) XA 協(xié)議的支持。
這里,我們從三個(gè)方面來(lái)深入解讀這個(gè)新的特性:
- 是什么(What):XA 模式是什么?
- 為什么(Why):為什么支持 XA?
- 怎么做(How):XA 模式是如何實(shí)現(xiàn)的,以及怎樣使用?
1. XA 模式是什么?
這里有兩個(gè)基本的前置概念:
- 什么是 XA?
- 什么是 Seata 定義的所謂 事務(wù)模式?
基于這兩點(diǎn),再來(lái)理解 XA 模式就很自然了。
1.1 什么是 XA?
XA 規(guī)范 是 X/Open 組織定義的分布式事務(wù)處理(DTP,Distributed Transaction Processing)標(biāo)準(zhǔn)。
XA 規(guī)范 描述了全局的事務(wù)管理器與局部的資源管理器之間的接口。 XA規(guī)范 的目的是允許的多個(gè)資源(如數(shù)據(jù)庫(kù),應(yīng)用服務(wù)器,消息隊(duì)列等)在同一事務(wù)中訪問(wèn),這樣可以使 ACID 屬性跨越應(yīng)用程序而保持有效。
XA 規(guī)范 使用兩階段提交(2PC,Two-Phase Commit)來(lái)保證所有資源同時(shí)提交或回滾任何特定的事務(wù)。
XA 規(guī)范 在上世紀(jì) 90 年代初就被提出。目前,幾乎所有主流的數(shù)據(jù)庫(kù)都對(duì) XA 規(guī)范 提供了支持。
1.2 什么是 Seata 的事務(wù)模式?
Seata 定義了全局事務(wù)的框架。
全局事務(wù) 定義為若干 分支事務(wù) 的整體協(xié)調(diào):
- TM 向 TC 請(qǐng)求發(fā)起(Begin)、提交(Commit)、回滾(Rollback)全局事務(wù)。
- TM 把代表全局事務(wù)的 XID 綁定到分支事務(wù)上。
- RM 向 TC 注冊(cè),把分支事務(wù)關(guān)聯(lián)到 XID 代表的全局事務(wù)中。
- RM 把分支事務(wù)的執(zhí)行結(jié)果上報(bào)給 TC。(可選)
- TC 發(fā)送分支提交(Branch Commit)或分支回滾(Branch Rollback)命令給 RM。
Seata 的 全局事務(wù) 處理過(guò)程,分為兩個(gè)階段:
- 執(zhí)行階段 :執(zhí)行 分支事務(wù),并 保證 執(zhí)行結(jié)果滿(mǎn)足是 可回滾的(Rollbackable) 和 持久化的(Durable)。
- 完成階段: 根據(jù) 執(zhí)行階段 結(jié)果形成的決議,應(yīng)用通過(guò) TM 發(fā)出的全局提交或回滾的請(qǐng)求給 TC,TC 命令 RM 驅(qū)動(dòng) 分支事務(wù) 進(jìn)行 Commit 或 Rollback。
Seata 的所謂 事務(wù)模式 是指:運(yùn)行在 Seata 全局事務(wù)框架下的 分支事務(wù) 的行為模式。準(zhǔn)確地講,應(yīng)該叫作 分支事務(wù)模式。
不同的 事務(wù)模式 區(qū)別在于 分支事務(wù) 使用不同的方式達(dá)到全局事務(wù)兩個(gè)階段的目標(biāo)。即,回答以下兩個(gè)問(wèn)題:
- 執(zhí)行階段 :如何執(zhí)行并 保證 執(zhí)行結(jié)果滿(mǎn)足是 可回滾的(Rollbackable) 和 持久化的(Durable)。
- 完成階段: 收到 TC 的命令后,如何做到分支的提交或回滾?
以我們 Seata 的 AT 模式和 TCC 模式為例來(lái)理解:
AT 模式
執(zhí)行階段:
- 可回滾:根據(jù) SQL 解析結(jié)果,記錄回滾日志
- 持久化:回滾日志和業(yè)務(wù) SQL 在同一個(gè)本地事務(wù)中提交到數(shù)據(jù)庫(kù)
完成階段:
- 分支提交:異步刪除回滾日志記錄
- 分支回滾:依據(jù)回滾日志進(jìn)行反向補(bǔ)償更新
TCC 模式
執(zhí)行階段:
- 調(diào)用業(yè)務(wù)定義的 Try 方法(完全由業(yè)務(wù)層面保證 可回滾 和 持久化)
完成階段:
- 分支提交:調(diào)用各事務(wù)分支定義的 Confirm 方法
- 分支回滾:調(diào)用各事務(wù)分支定義的 Cancel 方法
1.3 什么是 Seata 的 XA 模式?
XA 模式:
在 Seata 定義的分布式事務(wù)框架內(nèi),利用事務(wù)資源(數(shù)據(jù)庫(kù)、消息服務(wù)等)對(duì) XA 協(xié)議的支持,以 XA 協(xié)議的機(jī)制來(lái)管理分支事務(wù)的一種 事務(wù)模式。
執(zhí)行階段:
- 可回滾:業(yè)務(wù) SQL 操作放在 XA 分支中進(jìn)行,由資源對(duì) XA 協(xié)議的支持來(lái)保證 可回滾
- 持久化:XA 分支完成后,執(zhí)行 XA prepare,同樣,由資源對(duì) XA 協(xié)議的支持來(lái)保證 持久化(即,之后任何意外都不會(huì)造成無(wú)法回滾的情況)
完成階段:
- 分支提交:執(zhí)行 XA 分支的 commit
- 分支回滾:執(zhí)行 XA 分支的 rollback
2. 為什么支持 XA?
為什么要在 Seata 中增加 XA 模式呢?支持 XA 的意義在哪里呢?
2.1 補(bǔ)償型事務(wù)模式的問(wèn)題
本質(zhì)上,Seata 已經(jīng)支持的 3 大事務(wù)模式:AT、TCC、Saga 都是 補(bǔ)償型 的。
補(bǔ)償型 事務(wù)處理機(jī)制構(gòu)建在 事務(wù)資源 之上(要么在中間件層面,要么在應(yīng)用層面),事務(wù)資源 本身對(duì)分布式事務(wù)是無(wú)感知的。
事務(wù)資源 對(duì)分布式事務(wù)的無(wú)感知存在一個(gè)根本性的問(wèn)題:無(wú)法做到真正的 全局一致性 。
比如,一條庫(kù)存記錄,處在 補(bǔ)償型 事務(wù)處理過(guò)程中,由 100 扣減為 50。此時(shí),倉(cāng)庫(kù)管理員連接數(shù)據(jù)庫(kù),查詢(xún)統(tǒng)計(jì)庫(kù)存,就看到當(dāng)前的 50。之后,事務(wù)因?yàn)楫愅饣貪L,庫(kù)存會(huì)被補(bǔ)償回滾為 100。顯然,倉(cāng)庫(kù)管理員查詢(xún)統(tǒng)計(jì)到的 50 就是 臟 數(shù)據(jù)。
可以看到,補(bǔ)償型 分布式事務(wù)機(jī)制因?yàn)椴灰?事務(wù)資源 本身(如數(shù)據(jù)庫(kù))的機(jī)制參與,所以無(wú)法保證從事務(wù)框架之外的全局視角的數(shù)據(jù)一致性。
2.2 XA 的價(jià)值
與 補(bǔ)償型 不同,XA 協(xié)議 要求 事務(wù)資源 本身提供對(duì)規(guī)范和協(xié)議的支持。
因?yàn)?事務(wù)資源 感知并參與分布式事務(wù)處理過(guò)程,所以 事務(wù)資源(如數(shù)據(jù)庫(kù))可以保障從任意視角對(duì)數(shù)據(jù)的訪問(wèn)有效隔離,滿(mǎn)足全局?jǐn)?shù)據(jù)一致性。
比如,上一節(jié)提到的庫(kù)存更新場(chǎng)景,XA 事務(wù)處理過(guò)程中,中間態(tài)數(shù)據(jù)庫(kù)存 50 由數(shù)據(jù)庫(kù)本身保證,是不會(huì)倉(cāng)庫(kù)管理員的查詢(xún)統(tǒng)計(jì) 看 到的。(當(dāng)然隔離級(jí)別需要 讀已提交 以上)
除了 全局一致性 這個(gè)根本性的價(jià)值外,支持 XA 還有如下幾個(gè)方面的好處:
- 業(yè)務(wù)無(wú)侵入:和 AT 一樣,XA 模式將是業(yè)務(wù)無(wú)侵入的,不給應(yīng)用設(shè)計(jì)和開(kāi)發(fā)帶來(lái)額外負(fù)擔(dān)。
- 數(shù)據(jù)庫(kù)的支持廣泛:XA 協(xié)議被主流關(guān)系型數(shù)據(jù)庫(kù)廣泛支持,不需要額外的適配即可使用。
- 多語(yǔ)言支持容易:因?yàn)椴簧婕?SQL 解析,XA 模式對(duì) Seata 的 RM 的要求比較少,為不同語(yǔ)言開(kāi)發(fā) SDK 較之 AT 模式將更 薄,更容易。
- 傳統(tǒng)基于 XA 應(yīng)用的遷移:傳統(tǒng)的,基于 XA 協(xié)議的應(yīng)用,遷移到 Seata 平臺(tái),使用 XA 模式將更平滑。
2.3 XA 廣泛被質(zhì)疑的問(wèn)題
不存在某一種分布式事務(wù)機(jī)制可以完美適應(yīng)所有場(chǎng)景,滿(mǎn)足所有需求。
XA 規(guī)范早在上世紀(jì) 90 年代初就被提出,用以解決分布式事務(wù)處理這個(gè)領(lǐng)域的問(wèn)題。
現(xiàn)在,無(wú)論 AT 模式、TCC 模式還是 Saga 模式,這些模式的提出,本質(zhì)上都源自 XA 規(guī)范對(duì)某些場(chǎng)景需求的無(wú)法滿(mǎn)足。
XA 規(guī)范定義的分布式事務(wù)處理機(jī)制存在一些被廣泛質(zhì)疑的問(wèn)題,針對(duì)這些問(wèn)題,我們是如何思考的呢?
1. 數(shù)據(jù)鎖定:數(shù)據(jù)在整個(gè)事務(wù)處理過(guò)程結(jié)束前,都被鎖定,讀寫(xiě)都按隔離級(jí)別的定義約束起來(lái)。
思考:
數(shù)據(jù)鎖定是獲得更高隔離性和全局一致性所要付出的代價(jià)。
補(bǔ)償型 的事務(wù)處理機(jī)制,在 執(zhí)行階段 即完成分支(本地)事務(wù)的提交,(資源層面)不鎖定數(shù)據(jù)。而這是以犧牲 隔離性 為代價(jià)的。
另外,AT 模式使用 全局鎖 保障基本的 寫(xiě)隔離,實(shí)際上也是鎖定數(shù)據(jù)的,只不過(guò)鎖在 TC 側(cè)集中管理,解鎖效率高且沒(méi)有阻塞的問(wèn)題。
2. 協(xié)議阻塞:XA prepare 后,分支事務(wù)進(jìn)入阻塞階段,收到 XA commit 或 XA rollback 前必須阻塞等待。
思考:
協(xié)議的阻塞機(jī)制本身并不是問(wèn)題,關(guān)鍵問(wèn)題在于 協(xié)議阻塞 遇上 數(shù)據(jù)鎖定。
如果一個(gè)參與全局事務(wù)的資源 “失聯(lián)” 了(收不到分支事務(wù)結(jié)束的命令),那么它鎖定的數(shù)據(jù),將一直被鎖定。進(jìn)而,甚至可能因此產(chǎn)生死鎖。
這是 XA 協(xié)議的核心痛點(diǎn),也是 Seata 引入 XA 模式要重點(diǎn)解決的問(wèn)題。
基本思路是兩個(gè)方面:避免 “失聯(lián)” 和 增加 “自解鎖” 機(jī)制。(這里涉及非常多技術(shù)細(xì)節(jié),暫時(shí)不展開(kāi),在后續(xù) XA 模式演進(jìn)過(guò)程中,會(huì)專(zhuān)門(mén)拿出來(lái)討論)
3. 性能差:性能的損耗主要來(lái)自?xún)蓚€(gè)方面:一方面,事務(wù)協(xié)調(diào)過(guò)程,增加單個(gè)事務(wù)的 RT;另一方面,并發(fā)事務(wù)數(shù)據(jù)的鎖沖突,降低吞吐。
思考:
和不使用分布式事務(wù)支持的運(yùn)行場(chǎng)景比較,性能肯定是下降的,這點(diǎn)毫無(wú)疑問(wèn)。
本質(zhì)上,事務(wù)(無(wú)論是本地事務(wù)還是分布式事務(wù))機(jī)制就是拿部分 性能的犧牲 ,換來(lái) 編程模型的簡(jiǎn)單 。
與同為 業(yè)務(wù)無(wú)侵入 的 AT 模式比較:
首先,因?yàn)橥瑯舆\(yùn)行在 Seata 定義的分布式事務(wù)框架下,XA 模式并沒(méi)有產(chǎn)生更多事務(wù)協(xié)調(diào)的通信開(kāi)銷(xiāo)。
其次,并發(fā)事務(wù)間,如果數(shù)據(jù)存在熱點(diǎn),產(chǎn)生鎖沖突,這種情況,在 AT 模式(默認(rèn)使用全局鎖)下同樣存在的。
所以,在影響性能的兩個(gè)主要方面,XA 模式并不比 AT 模式有非常明顯的劣勢(shì)。
AT 模式性能優(yōu)勢(shì)主要在于:集中管理全局?jǐn)?shù)據(jù)鎖,鎖的釋放不需要 RM 參與,釋放鎖非???另外,全局提交的事務(wù),完成階段 異步化。
3. XA 模式如何實(shí)現(xiàn)以及怎樣用?
3.1 XA 模式的設(shè)計(jì)
3.1.1 設(shè)計(jì)目標(biāo)
XA 模式的基本設(shè)計(jì)目標(biāo),兩個(gè)主要方面:
- 從 場(chǎng)景 上,滿(mǎn)足 全局一致性 的需求。
- 從 應(yīng)用上,保持與 AT 模式一致的無(wú)侵入。
- 從 機(jī)制 上,適應(yīng)分布式微服務(wù)架構(gòu)的特點(diǎn)。
整體思路:
與 AT 模式相同的:以應(yīng)用程序中 本地事務(wù) 的粒度,構(gòu)建到 XA 模式的 分支事務(wù)。
通過(guò)數(shù)據(jù)源代理,在應(yīng)用程序本地事務(wù)范圍外,在框架層面包裝 XA 協(xié)議的交互機(jī)制,把 XA 編程模型 透明化。
把 XA 的 2PC 拆開(kāi),在分支事務(wù) 執(zhí)行階段 的末尾就進(jìn)行 XA prepare,把 XA 協(xié)議完美融合到 Seata 的事務(wù)框架,減少一輪 RPC 交互。
3.1.2 核心設(shè)計(jì)
1. 整體運(yùn)行機(jī)制
XA 模式 運(yùn)行在 Seata 定義的事務(wù)框架內(nèi):
執(zhí)行階段(E xecute):
- XA start/XA end/XA prepare + SQL + 注冊(cè)分支
完成階段(F inish):
- XA commit/XA rollback
2. 數(shù)據(jù)源代理
XA 模式需要 XAConnection。
獲取 XAConnection 兩種方式:
- 方式一:要求開(kāi)發(fā)者配置 XADataSource
- 方式二:根據(jù)開(kāi)發(fā)者的普通 DataSource 來(lái)創(chuàng)建
第一種方式,給開(kāi)發(fā)者增加了認(rèn)知負(fù)擔(dān),需要為 XA 模式專(zhuān)門(mén)去學(xué)習(xí)和使用 XA 數(shù)據(jù)源,與 透明化 XA 編程模型的設(shè)計(jì)目標(biāo)相違背。
第二種方式,對(duì)開(kāi)發(fā)者比較友好,和 AT 模式使用一樣,開(kāi)發(fā)者完全不必關(guān)心 XA 層面的任何問(wèn)題,保持本地編程模型即可。
我們優(yōu)先設(shè)計(jì)實(shí)現(xiàn)第二種方式:數(shù)據(jù)源代理根據(jù)普通數(shù)據(jù)源中獲取的普通 JDBC 連接創(chuàng)建出相應(yīng)的 XAConnection。
類(lèi)比 AT 模式的數(shù)據(jù)源代理機(jī)制,如下:
但是,第二種方法有局限:無(wú)法保證兼容的正確性。
實(shí)際上,這種方法是在做數(shù)據(jù)庫(kù)驅(qū)動(dòng)程序要做的事情。不同的廠商、不同版本的數(shù)據(jù)庫(kù)驅(qū)動(dòng)實(shí)現(xiàn)機(jī)制是廠商私有的,我們只能保證在充分測(cè)試過(guò)的驅(qū)動(dòng)程序上是正確的,開(kāi)發(fā)者使用的驅(qū)動(dòng)程序版本差異很可能造成機(jī)制的失效。
這點(diǎn)在 Oracle 上體現(xiàn)非常明顯。參見(jiàn) Druid issue:https://github.com/alibaba/druid/issues/3707
綜合考慮,XA 模式的數(shù)據(jù)源代理設(shè)計(jì)需要同時(shí)支持第一種方式:基于 XA 數(shù)據(jù)源進(jìn)行代理。
類(lèi)比 AT 模式的數(shù)據(jù)源代理機(jī)制,如下:
3. 分支注冊(cè)
XA start 需要 Xid 參數(shù)。
這個(gè) Xid 需要和 Seata 全局事務(wù)的 XID 和 BranchId 關(guān)聯(lián)起來(lái),以便由 TC 驅(qū)動(dòng) XA 分支的提交或回滾。
目前 Seata 的 BranchId 是在分支注冊(cè)過(guò)程,由 TC 統(tǒng)一生成的,所以 XA 模式分支注冊(cè)的時(shí)機(jī)需要在 XA start 之前。
將來(lái)一個(gè)可能的優(yōu)化方向:
把分支注冊(cè)盡量延后。類(lèi)似 AT 模式在本地事務(wù)提交之前才注冊(cè)分支,避免分支執(zhí)行失敗情況下,沒(méi)有意義的分支注冊(cè)。
這個(gè)優(yōu)化方向需要 BranchId 生成機(jī)制的變化來(lái)配合。BranchId 不通過(guò)分支注冊(cè)過(guò)程生成,而是生成后再帶著 BranchId 去注冊(cè)分支。
小結(jié)
這里只通過(guò)幾個(gè)重要的核心設(shè)計(jì),說(shuō)明 XA 模式的基本工作機(jī)制。
此外,還有包括 連接保持、異常處理 等重要方面,有興趣可以從項(xiàng)目代碼中進(jìn)一步了解。
以后會(huì)陸續(xù)寫(xiě)出來(lái)和大家交流。
3.1.3 演進(jìn)規(guī)劃
XA 模式總體的演進(jìn)規(guī)劃如下:
第 1 步(已經(jīng)完成):首個(gè)版本(1.2.0),把 XA 模式原型機(jī)制跑通。確保只增加,不修改,不給其他模式引入的新問(wèn)題。
第 2 步(計(jì)劃 5 月完成):與 AT 模式必要的融合、重構(gòu)。
第 3 步(計(jì)劃 7 月完成):完善異常處理機(jī)制,進(jìn)行上生產(chǎn)所必需的打磨。
第 4 步(計(jì)劃 8 月完成):性能優(yōu)化。
第 5 步(計(jì)劃 2020 年內(nèi)完成):結(jié)合 Seata 項(xiàng)目正在進(jìn)行的面向云原生的 Transaction Mesh 設(shè)計(jì),打造云原生能力。
3.2 XA 模式的使用
從編程模型上,XA 模式與 AT 模式保持完全一致。
可以參考 Seata 官網(wǎng)的樣例:seata-xa
樣例場(chǎng)景是 Seata 經(jīng)典的,涉及庫(kù)存、訂單、賬戶(hù) 3 個(gè)微服務(wù)的商品訂購(gòu)業(yè)務(wù)。
在樣例中,上層編程模型與 AT 模式完全相同。只需要修改數(shù)據(jù)源代理,即可實(shí)現(xiàn) XA 模式與 AT 模式之間的切換。
- @Bean("dataSourceProxy")
- public DataSource dataSource(DruidDataSource druidDataSource) {
- // DataSourceProxy for AT mode
- // return new DataSourceProxy(druidDataSource);
- // DataSourceProxyXA for XA mode
- return new DataSourceProxyXA(druidDataSource);
- }
4. 總結(jié)
在當(dāng)前的技術(shù)發(fā)展階段,不存一個(gè)分布式事務(wù)處理機(jī)制可以完美滿(mǎn)足所有場(chǎng)景的需求。
一致性、可靠性、易用性、性能等諸多方面的系統(tǒng)設(shè)計(jì)約束,需要用不同的事務(wù)處理機(jī)制去滿(mǎn)足。
Seata 項(xiàng)目最核心的價(jià)值在于:構(gòu)建一個(gè)全面解決分布式事務(wù)問(wèn)題的 標(biāo)準(zhǔn)化 平臺(tái)。
基于 Seata,上層應(yīng)用架構(gòu)可以根據(jù)實(shí)際場(chǎng)景的需求,靈活選擇合適的分布式事務(wù)解決方案。
XA 模式的加入,補(bǔ)齊了 Seata 在 全局一致性 場(chǎng)景下的缺口,形成 AT、TCC、Saga、XA 四大 事務(wù)模式 的版圖,基本可以滿(mǎn)足所有場(chǎng)景的分布式事務(wù)處理訴求。
當(dāng)然 XA 模式和 Seata 項(xiàng)目本身都還不盡完美,有很多需要改進(jìn)和完善的地方。非常歡迎大家參與到項(xiàng)目的建設(shè)中,共同打造一個(gè)標(biāo)準(zhǔn)化的分布式事務(wù)平臺(tái)。
作者簡(jiǎn)介:煊檍,GitHub ID:sharajava,阿里巴巴中件間 GTS 研發(fā)團(tuán)隊(duì)負(fù)責(zé)人,SEATA 開(kāi)源項(xiàng)目發(fā)起人,曾在 Oracle 北京研發(fā)中心多年,從事 WebLogic 核心研發(fā)工作。長(zhǎng)期專(zhuān)注于中間件,尤其是分布式事務(wù)領(lǐng)域的技術(shù)實(shí)踐。
本文轉(zhuǎn)載自微信公眾號(hào)「高可用架構(gòu)」,可以通過(guò)以下二維碼關(guān)注。轉(zhuǎn)載本文請(qǐng)聯(lián)系高可用架構(gòu)公眾號(hào)。