一浩村、背景
隨著微服務(wù)架構(gòu)的興起露戒,越來越多的公司都對自身的業(yè)務(wù)架構(gòu)進(jìn)行了微服務(wù)化。在微服務(wù)架構(gòu)中白嘁,隨著服務(wù)的逐漸拆分坑鱼,數(shù)據(jù)庫的私有化已成為業(yè)界不成文的規(guī)定。因此伴隨著微服務(wù)拆分所帶來的數(shù)據(jù)一致性的問題也愈發(fā)嚴(yán)重絮缅,如何解決該問題成為微服務(wù)架構(gòu)落地過程中一個非常重要的問題鲁沥。由此我們引出分布式事務(wù)這一概念呼股,用來解決上述背景帶來的問題。在介紹分布式事務(wù)之前先讓我們回顧一下什么是事務(wù)画恰。
二彭谁、事務(wù)
事務(wù)是數(shù)據(jù)庫操作的最小工作單元,是作為單個邏輯工作單元執(zhí)行的一系列操作允扇;這些操作作為一個整體一起向系統(tǒng)提交缠局,要么都執(zhí)行、要么都不執(zhí)行蔼两;事務(wù)具有ACID四大屬性甩鳄。
A(Atomic):原子性,構(gòu)成事務(wù)的所有操作额划,要么都執(zhí)行完成妙啃,要么全部不執(zhí)行,不可能出現(xiàn)部分成功部分失敗的情況俊戳。
C(Consistency):一致性揖赴,在事務(wù)執(zhí)行前后,數(shù)據(jù)庫的一致性約束沒有被破壞抑胎。比如:數(shù)據(jù)庫約束賬戶余額必須大于0燥滑,所以設(shè)置為無符號數(shù),在此約束下阿逃,A只有100元铭拧,但要轉(zhuǎn)出200元,此時數(shù)據(jù)庫會保持對約束的一致性恃锉,觸發(fā)執(zhí)行回滾搀菩。
I(Isolation):隔離性,數(shù)據(jù)庫中的事務(wù)一般都是并發(fā)的破托,隔離性是指并發(fā)的兩個事務(wù)的執(zhí)行互不干擾肪跋,一個事務(wù)不能看到其他事務(wù)的運行過程的中間狀態(tài)。通過配置事務(wù)隔離級別可以比避免臟讀土砂、重復(fù)讀問題州既。
D(Durability):持久性,事務(wù)完成之后萝映,該事務(wù)對數(shù)據(jù)的更改會持久到數(shù)據(jù)庫吴叶,且不會被回滾。
了解完了事務(wù)的基本概念序臂,接著讓我們看看什么是分布式事務(wù)蚌卤。
三、分布式事務(wù)
在分布式系統(tǒng)中,一個應(yīng)用系統(tǒng)拆分為獨立部署的多個服務(wù)造寝,因此需要服務(wù)與服務(wù)之間遠(yuǎn)程協(xié)作才能完成事務(wù)操作,這種分布式系統(tǒng)環(huán)境下由不同的服務(wù)之間通過網(wǎng)絡(luò)遠(yuǎn)程協(xié)作完成的事務(wù)稱為分布式事務(wù)吭练。
從架構(gòu)角度出發(fā)诫龙,分布式事務(wù)基本涉及到兩大類。
第一類:一個事務(wù)請求只涉及單體服務(wù)鲫咽,但是會操作多張數(shù)據(jù)庫表签赃,多個數(shù)據(jù)庫表操作完成才表示最終完成。
第二類:一個事務(wù)涉及多個服務(wù)分尸,同時每個服務(wù)可能連接著一個或者多個數(shù)據(jù)庫锦聊,需要協(xié)同多個獨立的服務(wù)訪問多個數(shù)據(jù)存儲最終才能完成。
我們常見的分布式事務(wù)來保證數(shù)據(jù)的一致性的方法分為兩類:強一致性箩绍、最終一致性孔庭。
采用強一致性的分布式事務(wù)的方案:通常采用兩段式提交協(xié)議2PC、三段式提交協(xié)議3PC材蛛。在微服務(wù)架構(gòu)中圆到,該種方式不太適合,原因如下:
由于微服務(wù)間無法直接進(jìn)行數(shù)據(jù)訪問卑吭,微服務(wù)間互相調(diào)用通常通過RPC或Http API進(jìn)行芽淡,所以已經(jīng)無法使用TM統(tǒng)一管理微服務(wù)的RM
不同的微服務(wù)使用的數(shù)據(jù)源類型可能完全不同,如果微服務(wù)使用了NoSQL之類不原生支持事務(wù)的數(shù)據(jù)庫豆赏,業(yè)務(wù)的事務(wù)很難實現(xiàn)
即使微服務(wù)使用的數(shù)據(jù)源都支持事務(wù)挣菲,那么如果使用一個大事務(wù)將許多微服務(wù)的事務(wù)管理起來,這個大事務(wù)維持的時間掷邦,將比本地事務(wù)長幾個數(shù)量級白胀。如此長時間的事務(wù)及跨服務(wù)的事務(wù),將為產(chǎn)生很多鎖及數(shù)據(jù)不可用耙饰,嚴(yán)重影響系統(tǒng)性能
因此我們一般采用最終一致性來保證分布式系統(tǒng)的一致性纹笼。
常見的最終一致性的分布式事務(wù)解決方案有:事件通知模式(本地異步事件服務(wù)模式、外部事件服務(wù)模式苟跪、MQ事務(wù)消息模式廷痘、最大努力通知模式)、事務(wù)補償模式(Saga件已、TCC)笋额。我們通過調(diào)研上述解決方案總結(jié)出了以下特性:
下面我們通過一個業(yè)務(wù)場景來了解一下什么是分布式事務(wù),并且我們創(chuàng)新行業(yè)是用什么方案來解決數(shù)據(jù)一致性問題的篷扩。
四兄猩、業(yè)務(wù)場景
假設(shè)有一個積分簽到系統(tǒng),里面有一個簽到兌換積分從而兌換物品的功能場景。
我們要做的事情如下:
用戶簽到成功枢冤、增加用戶積分
用戶創(chuàng)建兌換物品訂單鸠姨,訂單狀態(tài)為已支付
扣除用戶積分
扣減物品庫存
創(chuàng)建物流出庫單
針對以上的業(yè)務(wù)場景,我們應(yīng)該如何實現(xiàn)分布式事務(wù)來滿足業(yè)務(wù)需要淹真。下面主要講事務(wù)補償模式來實現(xiàn)分布式事務(wù)讶迁。
4.1 TCC 模式
TCC是將整體業(yè)務(wù)邏輯的每一個事務(wù)提交分成了Try,Confirm核蘸,Cancel三個操作巍糯。
Try:完成業(yè)務(wù)的準(zhǔn)備工作
Confirm:完成業(yè)務(wù)的提交工作
Cancel:完成業(yè)務(wù)的回滾工作
針對上述業(yè)務(wù)場景,按照業(yè)務(wù)背景要做的事情客扎,實現(xiàn)一個TCC的分布式事務(wù)祟峦。
如果要實現(xiàn)一個TCC的分布式事務(wù),首先要做的是了解業(yè)務(wù)的主流程以及各個接口提供的業(yè)務(wù)含義徙鱼,不直接完成這個業(yè)務(wù)操作宅楞,而是完成一個Try(預(yù)處理)操作。
例如:給用戶添加積分疆偿,我們不直接添加積分咱筛,先預(yù)處理添加積分「斯剩扣物品庫存我們也不直接扣庫存迅箩,先凍結(jié)將扣掉的庫存。具體如下圖
如果Try的邏輯都成功处铛,TCC開始執(zhí)行業(yè)務(wù)的Confirm操作饲趋,完成整個事務(wù)流程。添加用戶積分扣庫存等操作撤蟆。如果Try的邏輯部分成功奕塑,有部分有問題,那么開始執(zhí)行Cancel操作家肯,撤銷之前執(zhí)行的所有操作龄砰。
總體對于TCC模式來說,要做一個分布式事務(wù)讨衣,業(yè)務(wù)中的一個接口需要完成3個邏輯的改造换棚,Try-Confirm-Cancel。
服務(wù)調(diào)用鏈路依次執(zhí)行Try邏輯
如果都正常的話反镇,執(zhí)行Confirm邏輯固蚤,完成整個業(yè)務(wù)流程
如果部分服務(wù)的Try邏輯有問題,會執(zhí)行Cancel邏輯歹茶,撤銷之前執(zhí)行的所有操作
TCC模式對于業(yè)務(wù)的侵入性比較強夕玩,流程比較繁瑣你弦。各個業(yè)務(wù)側(cè)都需要支持升級。對于我們創(chuàng)新行業(yè)來說燎孟,成本有點大禽作,我們選擇了另一種模式來實現(xiàn)分布式事務(wù)——Saga模式。
4.2 Saga模式
Saga是一種純業(yè)務(wù)補償模式揩页,其設(shè)計理念為领迈,業(yè)務(wù)在調(diào)用的時候正常提交,當(dāng)一個服務(wù)失敗的時候碍沐,所有其依賴的上游服務(wù)都進(jìn)行業(yè)務(wù)補償操作。
Saga的基本概念:
saga:長事務(wù)衷蜓,long live transaction
每個本地事務(wù)有對應(yīng)的補償事務(wù)
執(zhí)行情況
正常:T1 -> T2 -> T3 -> … -> Tn
異常:T1 -> T2 -> T3(異常)-> C3 -> C2 -> C1
Saga兩種恢復(fù)策略:
backward recovery累提,向后恢復(fù),補償所有已完成的事務(wù)(回滾操作)
forward recovery磁浇,向前恢復(fù)斋陪,重試失敗的事務(wù),假設(shè)每個子事務(wù)最終都會成功(重試操作)
Saga事務(wù)的優(yōu)缺點:
優(yōu)點:模型比TCC更簡單置吓,只需業(yè)務(wù)方提供事務(wù)執(zhí)行接口transaction无虚、事務(wù)取消補償接口cancel
缺點:直接執(zhí)行事務(wù)執(zhí)行接口transaction,可能有副作用(無論是否回滾衍锚,都會執(zhí)行事務(wù)接口的邏輯友题,舉例:A賬號向B賬號轉(zhuǎn)賬,T1事務(wù)對A用戶扣款戴质,T2事務(wù)對B用戶加款度宦,T1執(zhí)行成功,同時產(chǎn)生了一條扣款記錄告匠,T2執(zhí)行失敗需要回滾T2和T1戈抄,在這個過程中的副作用是A賬號能感知到金額變化和扣款記錄)
針對上述業(yè)務(wù)背景,我們對于業(yè)務(wù)側(cè)只需要支持事務(wù)提交的接口( T )和失敗補償?shù)慕涌冢?C )即可后专。具體流程如下圖:
對于Saga事務(wù)來說只有完成跟未完成兩種狀態(tài)划鸽。無論是事務(wù)全部執(zhí)行成功,還是全部補償成功都視為完成狀態(tài)戚哎。出現(xiàn)異常導(dǎo)致流程中斷為未完成狀態(tài)裸诽。我們針對于業(yè)務(wù)提交的流程的Http Code狀態(tài)來區(qū)分是執(zhí)行向前恢復(fù)(重試),還是向后補償(回滾)建瘫。對于沒有補償 C 的業(yè)務(wù)崭捍,我們將采取向前恢復(fù),直到成功啰脚。對于異常情況殷蛇,使用離線補償?shù)姆绞綄ξ赐瓿傻腟aga事務(wù)進(jìn)行重做实夹,如長時間無法完成將觸發(fā)報警,人工處理粒梦。
我們行創(chuàng)基于上述saga模型研發(fā)了Saga的事務(wù)協(xié)調(diào)器亮航,具體執(zhí)行流程如下:
業(yè)務(wù)方使用需要實現(xiàn)對應(yīng)事務(wù)的執(zhí)行方法和補償方法,采用上報分布式事務(wù)的方式進(jìn)行操作匀们,事務(wù)上報的數(shù)據(jù)屬性簡介:
Name:本次流程的名稱
OID:請求流程ID缴淋,必須唯一
Process:每個子流程的屬性簡介(類型為list,按照順序執(zhí)行)
Transaction:執(zhí)行事務(wù)的流程
Call:服務(wù)調(diào)用的方式泄朴,HTTP調(diào)用時傳GET重抖、POST等
ServiceName:服務(wù)名稱
ServiceMethod:調(diào)用服務(wù)的方法名稱,HTTP傳請求的URL
Body:POST body祖灰,string類型
Query:Get Query钟沛,map[string]interfase{}
oid:訂單ID, 必傳局扶,必須唯一恨统,用于冪等性校驗等
Compensate:執(zhí)行失敗的補償流程
Call:服務(wù)調(diào)用的方式,HTTP調(diào)用時傳GET三妈、POST等
ServiceName:服務(wù)名稱
ServiceMethod:調(diào)用服務(wù)的方法名稱畜埋,HTTP傳請求的URL
Body:POST body,string類型
Query:Get Query畴蒲,map[string]interfase{}
oid:訂單ID悠鞍, 必傳,必須唯一模燥,用于冪等性校驗等
業(yè)務(wù)方按上述規(guī)則上報數(shù)據(jù)狞玛,該事務(wù)會最終保證數(shù)據(jù)一致性,要么全部成功涧窒,要么都失敗回滾心肪。
我們創(chuàng)新行業(yè)采用Saga模型來實現(xiàn)分布式事務(wù),來解決日常微服務(wù)拆分帶來的數(shù)據(jù)一致性的問題纠吴,滿足了我們?nèi)粘5漠a(chǎn)品功能的需要硬鞍,解決了棘手的問題。
五戴已、總結(jié)
針對分布式事務(wù)的解決方案都有各自的特點固该,沒有一個最優(yōu)的方案,需要結(jié)合實際的應(yīng)用場景選擇合適的模式糖儡。
2PC 和 3PC 是一種強一致性事務(wù)伐坏,不過還是有數(shù)據(jù)不一致,阻塞等風(fēng)險握联,而且只能用在數(shù)據(jù)庫層面桦沉。
Saga每瞒、TCC是一種補償性事務(wù)的思想,對業(yè)務(wù)入侵較大纯露,需要業(yè)務(wù)方實現(xiàn)對應(yīng)的方法剿骨。
本地消息、事務(wù)消息和最大努力通知其實都是最終一致性事務(wù)埠褪,因此適用于一些對時間不敏感的業(yè)務(wù)浓利。