Go開源說第十七期 分布式事務(wù)DTM
大家好,很高興到“Go開源說”跟大家分享DTM,我是葉東富,DTM作者,github:https://github.com/yedf
內(nèi)容提綱
本次分享分為以下四個(gè)部分:
DTM是什么
產(chǎn)生的背景
可以解決什么問題
發(fā)展現(xiàn)狀與未來
DTM是什么
我們按照開源項(xiàng)目的習(xí)慣,以一個(gè)類似Quick Start的例子來說明:
一個(gè)例子
業(yè)務(wù)場景:A跨行轉(zhuǎn)給B 30元,A、B分屬不同銀行
需求要點(diǎn):
需要保證A-30和B+30,都成功,或都失敗
中間任何一個(gè)地方發(fā)生故障,不能影響最終的數(shù)據(jù)一致性
因數(shù)據(jù)保存在多個(gè)數(shù)據(jù)庫實(shí)例,無法通過本地事務(wù)解決
解決方案DTM:A Distributed Transaction Manager
req := &gin.H{"amount": 30} // 微服務(wù)的載荷// DtmServer為DTM服務(wù)的地址saga := dtmcli.NewSaga(DtmServer, dtmcli.MustGenGid(DtmServer)).// 添加一個(gè)TransOut的子事務(wù),正向操作為url: qsBusi+"/TransOut", 逆向操作為url: qsBusi+"/TransOutCompensate"Add(qsBusi+"/TransOut", qsBusi+"/TransOutCompensate", req).// 添加一個(gè)TransIn的子事務(wù),正向操作為url: qsBusi+"/TransIn", 逆向操作為url: qsBusi+"/TransInCompensate"Add(qsBusi+"/TransIn", qsBusi+"/TransInCompensate", req)// 提交saga事務(wù),dtm會(huì)完成所有的子事務(wù)/回滾所有的子事務(wù)err := saga.Submit()
在這個(gè)例子中:
定義了一個(gè)Saga事務(wù)
Saga事務(wù)中的第一個(gè)子事務(wù)TransOut負(fù)責(zé)扣減A的余額
Saga事務(wù)中的第一個(gè)子事務(wù)TransIn負(fù)責(zé)增加B的余額
完成了Saga事務(wù)定義之后,將事務(wù)提交給DTM
DTM將按順序完成各項(xiàng)子事務(wù)
如果中間出現(xiàn)臨時(shí)的網(wǎng)絡(luò)錯(cuò)誤,DTM會(huì)負(fù)責(zé)重試
如果業(yè)務(wù)返回失敗,DTM會(huì)調(diào)用子事務(wù)的補(bǔ)償操作,保證最終數(shù)據(jù)一致性
跨數(shù)據(jù)源、跨服務(wù)的數(shù)據(jù)一致性
DTM是跨數(shù)據(jù)源、跨服務(wù)的數(shù)據(jù)一致性解決方案,他又可以分為以下三類:跨數(shù)據(jù)庫、跨服務(wù)、混合

易混淆的分布式數(shù)據(jù)庫事務(wù)
現(xiàn)在涌現(xiàn)出構(gòu)建在分布式系統(tǒng)上的數(shù)據(jù)庫--NewSQL,能夠隨著分布式系統(tǒng)的動(dòng)態(tài)擴(kuò)容而大幅提高系統(tǒng)的服務(wù)能力,例如谷歌的Spanner、國產(chǎn)的TiDB。NewSQL的事務(wù)是在分布式系統(tǒng)上構(gòu)建的,也被稱之為分布式事務(wù),但是與我們在前面介紹的分布式事務(wù)在解決問題類型不同,所使用的核心技術(shù)也不同。
NewSQL事務(wù)主要解決的是數(shù)據(jù)庫擴(kuò)展到多Host下的ACID,聚焦在數(shù)據(jù)庫系統(tǒng)層面,而DTM聚焦在應(yīng)用層和多數(shù)據(jù)源之間的數(shù)據(jù)一致性。
產(chǎn)生的背景
隨著我們公司業(yè)務(wù)的發(fā)展,交易系統(tǒng)越來越復(fù)雜,交易并發(fā)也越來越高。原先一個(gè)交易是放在一個(gè)本地事務(wù)里完成,由數(shù)據(jù)庫來保證ACID。這種架構(gòu),一方面要求把所有訂單交易的操作在一個(gè)服務(wù),導(dǎo)致無法微服務(wù)化,這樣就會(huì)把所有訂單涉及的子系統(tǒng)耦合進(jìn)訂單中,復(fù)雜度很高;另一方面,訂單修改庫存到訂單提交的這段時(shí)間里,庫存是被鎖定的,購買同一個(gè)商品的所有訂單會(huì)因?yàn)榭蹘齑娴男墟i導(dǎo)致串行,并發(fā)度低。
我們系統(tǒng)采用的語言棧是Node,調(diào)研之后,決定之后采用Go作為主棧。如果把交易系統(tǒng)全部用Go改寫之后再上線,周期十分漫長,對業(yè)務(wù)非常不友好。我們需要做漸進(jìn)式的改造,需要分布式事務(wù)支持一部分服務(wù)Go編寫,一部分服務(wù)Node編寫,然后把相關(guān)服務(wù)組合成一個(gè)全局的分布式事務(wù)。
當(dāng)前可選方案
我們調(diào)研了當(dāng)時(shí)市面上的方案,最成熟、應(yīng)用最廣泛的是阿里開源的Seata,它是Java語言開發(fā)的,如果我們選用這個(gè)方案的話,需要把訂單相關(guān)的系統(tǒng),全部改造成Java,工作量巨大,預(yù)計(jì)要20人月以上。還有其他方案,例如hmily、tcc-transaction、ByteTCC,這些都是Java的方案,工作量都接近。
其他語言的分布式事務(wù)方案,沒有看到成熟可用的,Go、PHP、Python、Node等語言搜索了一個(gè)遍,都未發(fā)現(xiàn)好的方案。
加上前面我們希望要一個(gè)漸進(jìn)式的方案,需要支持多語言的子服務(wù)組合成一個(gè)全局事務(wù),那么市面上的方案就更不可能了。綜合評估下來,我們決定自己開發(fā)
DTM架構(gòu)
我們開發(fā)的DTM架構(gòu)如下:

圖中左上角的Go客戶端SDK等,都是輕量級的SDK,大約幾十到幾百行代碼,新增一門語言支持非常容易。DTM服務(wù)器采用云原生友好的無狀態(tài)服務(wù),可以非常方便的動(dòng)態(tài)擴(kuò)容、做高可用,數(shù)據(jù)存儲(chǔ)在數(shù)據(jù)庫,和現(xiàn)在絕大多數(shù)公司的存儲(chǔ)架構(gòu)一致。
我們支持了類似如下的多語言,能夠采用各種語言編寫子服務(wù),最終組合成一個(gè)全局事務(wù):

可以解決什么問題
DTM提供了跨數(shù)據(jù)源分布式事務(wù)的一站式解決方案,支持的事務(wù)模式包括:可靠消息、事務(wù)消息、TCC、SAGA、XA。而在這些事務(wù)模式中,有一個(gè)難點(diǎn)問題是如果中間發(fā)生異常,導(dǎo)致子事務(wù)亂序要怎么解決。
異常
典型的異常分為三類,:
空補(bǔ)償:可能出現(xiàn)回滾請求在正向操作前到達(dá),此時(shí)需忽略
懸掛:可能出現(xiàn)回滾后,再收到正向操作,此時(shí)需忽略
冪等:可能出現(xiàn)重復(fù)請求,需要允許操作重試,不影響結(jié)果
通俗一點(diǎn)說,轉(zhuǎn)賬這類的分布式事務(wù),可能會(huì)出現(xiàn)服務(wù)先收到撤銷轉(zhuǎn)賬的請求,然后再收到轉(zhuǎn)賬的請求。下面是一個(gè)時(shí)序圖,很好的演示了異常的情況:

其中:
5 Cancel在Try之前被調(diào)用,是空補(bǔ)償
7 Cancel和5的重復(fù),是重復(fù)請求
8 Try在Cancel之后執(zhí)行,是懸掛操作
目前沒有看到哪個(gè)項(xiàng)目在這方面有成熟好用的解決方案,包括Seata,都要求業(yè)務(wù)層面直接處理這類問題。業(yè)務(wù)直接處理這類問題,面臨大量邏輯判斷,復(fù)雜易出錯(cuò),需要花費(fèi)大量的時(shí)間進(jìn)行調(diào)試測試,而且由于懸掛這樣的問題難以模擬,可能要線上跑一段時(shí)間后才會(huì)出現(xiàn)。業(yè)務(wù)對于這樣的情況只能表示很無奈,很頭疼。
子事務(wù)屏障
DTM首創(chuàng)了子事務(wù)屏障,系統(tǒng)性的解決了上述的異常問題,效果示意圖如下:

所有這些請求,到了子事務(wù)屏障后:不正常的請求,會(huì)被過濾;正常請求,通過屏障。開發(fā)者使用子事務(wù)屏障之后,前面所說的各種異常全部被妥善處理,業(yè)務(wù)開發(fā)人員只需要關(guān)注實(shí)際的業(yè)務(wù)邏輯,負(fù)擔(dān)大大降低。
子事務(wù)屏障提供了方法BranchBarrier.Call,方法的原型為:
func (bb *BranchBarrier) Call(db *sql.DB, busiCall BusiFunc) error業(yè)務(wù)開發(fā)人員,在busiCall里面編寫自己的相關(guān)邏輯,調(diào)用BranchBarrier.Call。BranchBarrier.Call保證,在空回滾、懸掛等場景下,busiCall不會(huì)被調(diào)用;在業(yè)務(wù)被重復(fù)調(diào)用時(shí),有冪等控制,保證只被提交一次。
子事務(wù)屏障會(huì)管理TCC、SAGA、事務(wù)消息等,也可以擴(kuò)展到其他領(lǐng)域
有了子事務(wù)屏障的幫助,分布式事務(wù)的使用門檻大大降低,原先需要架構(gòu)師才能勝任的工作,現(xiàn)在只需要普通開發(fā)人員就可以完成。未來DTM有可能成為推進(jìn)微服務(wù)化的核心組件。
說完了所有事務(wù)模式共同的問題和解決方案后,我們下面看一下DTM對各個(gè)事務(wù)模式的支持:
可靠消息、事務(wù)消息
這兩種事務(wù)模式保證消息至少被成功消費(fèi)一次(或者說相關(guān)服務(wù)的執(zhí)行最少成功一次),保證多個(gè)微服務(wù)被原子執(zhí)行,它適合不需要回滾的業(yè)務(wù)場景。例如:
點(diǎn)擊按鈕,領(lǐng)取優(yōu)惠券+會(huì)員,領(lǐng)優(yōu)惠券+會(huì)員不需要回滾,這種情況適合可靠消息
注冊成功后,領(lǐng)取優(yōu)惠券+會(huì)員,這種情況適合事務(wù)消息。
第二種情況和第一種對比,差別主要是只有注冊成功,才領(lǐng)取,如果注冊失敗,不可以領(lǐng)取,這種情況適合事務(wù)消息
DTM的支持如下:

如果是事務(wù)消息,那么在本地事務(wù)提交之前調(diào)用Prepare,提交之后調(diào)用Commit。如果沒有本地事務(wù),其實(shí)就是可靠消息,忽略Prepare調(diào)用即可。
XA
XA是標(biāo)準(zhǔn)二階段提交,一階段Prepare,二階段Commit/Rollback。一階段的所有Prepare成功,則Commit,否則Rollback。從修改數(shù)據(jù)開始,到Commit/Rollback結(jié)束,鎖定數(shù)據(jù)長,并發(fā)度較低。
這種事務(wù)模式適合并發(fā)不高的訂單轉(zhuǎn)賬等各項(xiàng)業(yè)務(wù)。
DTM的支持如下:

TCC
TCC的每個(gè)子事務(wù)有兩個(gè)階段:一階段Try;二階段Confirm/Cancel;Try中進(jìn)行資源預(yù)留,例如凍結(jié)資金;一階段如果全部成功,則Confirm,例如進(jìn)行余額扣減,解凍資金;一階段如果有一個(gè)子事務(wù)出現(xiàn)失敗,則Cancel,例如解凍資金;
TCC模式不長期鎖數(shù)據(jù),并發(fā)高。常用于支付、訂單類業(yè)務(wù)的拆分。
DTM的支持如下:

SAGA
SAGA每個(gè)子事務(wù)有正向分支和補(bǔ)償分支,它在正向分支中直接修改數(shù)據(jù),出錯(cuò)則補(bǔ)償所有修改過的數(shù)據(jù)。
SAGA比TCC少一個(gè)分支,一致性比TCC弱。適用于積分換禮品等不涉及資金業(yè)務(wù);也適用于對接較多第三方的長事務(wù)。
DTM的支持如下:

未支持的模式AT
AT為SEATA和部分其他的框架提供的事務(wù)模式,各方面特性類似于XA,性能比XA高,但低于其他模式。
這種模式的使用需要小心避免臟回滾,假如T1修改Row1,然后Row1被T2直接修改,接下來T1回滾,此時(shí)出現(xiàn)臟回滾,需要手動(dòng)處理。Seata給出避免這種臟回滾的辦法是,所有要修改Row1的訪問,都必須使用Seata中的Transaction注解,這個(gè)要求在多團(tuán)隊(duì)協(xié)作,新舊系統(tǒng)兼容上難以保證。
因?yàn)榕K回滾以及類似XA的特性,DTM支持了XA,并未支持AT模式,預(yù)計(jì)近期也不會(huì)支持。
DTM支持的其他場景
DTM還有一些其他典型的適用場景,例如:多個(gè)應(yīng)用間的對賬系統(tǒng)、發(fā)布文章后更新各類統(tǒng)計(jì)信息等
單服務(wù)多數(shù)據(jù)源
DTM對單服務(wù)多數(shù)據(jù)源的支持非常優(yōu)雅,只需要將每個(gè)數(shù)據(jù)源的修改用一個(gè)子事務(wù)屏障包裝起來即可。如圖所示:

發(fā)展現(xiàn)狀與未來
已完成的特性
從生產(chǎn)系統(tǒng)提煉而來,已接入三家
中英文文檔
HTTP/gRPC支持
測試覆蓋95%以上
Promethues監(jiān)控
完善的CI/CD
支持的語言包括Go、Python、PHP、Node、Java、C#等
完整的線上部署文檔,含直接部署、Docker、K8S
行業(yè)影響
干貨推廣文《分布式事務(wù)最經(jīng)典的其中解決方案》被大量的自媒體自發(fā)轉(zhuǎn)載(不是主動(dòng)投稿),大約有幾十個(gè)公眾號,多個(gè)自媒體平臺(tái)(包括脈脈,微博等)。該文章兩個(gè)月時(shí)間就排到谷歌搜索“分布式事務(wù)"結(jié)果第二。

DTM在Github上面的star增長迅速,受到了很大的關(guān)注

下圖是DTM和Seata在重要特性上的對比:

可以看到DTM在各項(xiàng)重要特性上不輸于,甚至優(yōu)于第一名,未來的發(fā)展?jié)摿艽?,他的目?biāo)是成為云原生架構(gòu)的核心組件
結(jié)束
我們的項(xiàng)目地址是:https://github.com/yedf/dtm
文檔是:https://dtm.pub
大家還可以通過掃碼進(jìn)群,一起交流~
