1.1 什麼是事務數據庫
事務能夠看作是一次大的活動,它由不一樣的小活動組成,這些活動要麼所有成功,要麼所有失敗。編程
1.2 本地事務服務器
在計算機系統中,更多的是經過關係型數據庫來控制事務,這是利用數據庫自己的事務特性來實現的,所以叫數據庫事務,因爲應用主要靠關係數據庫來控制事務,而數據庫一般和應用在同一個服務器,因此基於關係型數據庫的事務又被稱爲本地事務。網絡
數據庫事務的四大特性:ACID架構
A(Atomic):原子性,構成事務的全部操做,要麼都執行完成,要麼所有不執行,不可能出現部分紅功部分失敗的狀況。併發
C(Consistency):一致性,在事務執行先後,數據庫的一致性約束沒有被破壞。好比:張三向李四轉 100 元,轉帳前和轉帳後的數據是正確狀態這叫一致性,若是出現張三轉出 100 元,李四帳戶沒有增長 100 元這就出現了數 據錯誤,就沒有達到一致性。app
I(Isolation):隔離性,數據庫中的事務通常都是併發的,隔離性是指併發的兩個事務的執行互不干擾,一個事務不能看到其餘事務的運行過程的中間狀態。經過配置事務隔離級別能夠比避免髒讀、重複讀問題。框架
D(Durability):持久性,事務完成以後,該事務對數據的更改會持久到數據庫,且不會被回滾。異步
數據庫事務在實現時會將一次事務的全部操做所有歸入到一個不可分割的執行單元,該執行單元的全部操做要麼都成功,要麼都失敗,只要其中任一操做執行失敗,都將致使整個事務的回滾。分佈式
隨着互聯網的快速發展,軟件系統由原來的單體應用轉變爲分佈式應用,下圖描述了單體應用向微服務的演變:
分佈式系統會把一個應用系統拆分爲可獨立部署的多個服務,所以須要服務與服務之間遠程協做才能完成事務操做,這種分佈式系統環境下由不一樣的服務之間經過網絡遠程協做完成事務稱之爲分佈式事務,例如用戶註冊送積分事務、建立訂單減庫存事務,銀行轉帳事務等都是分佈式事務。
咱們知道本地事務依賴數據庫自己提供的事務特性來實現,所以如下邏輯能夠控制本地事務:
begin transaction; //1.本地數據庫操做:張三減小金額 //2.本地數據庫操做:李四增長金額commit transation;
可是在分佈式環境下,會變成下邊這樣:
begin transaction; //1.本地數據庫操做:張三減小金額 //2.遠程調用:讓李四增長金額commit transation;
能夠設想,當遠程調用讓李四增長金額成功了,因爲網絡問題遠程調用並無返回,此時本地事務提交失敗就回滾了張三減小金額的操做,此時張三和李四的數據就不一致了。
所以在分佈式架構的基礎上,傳統數據庫事務就沒法使用了,張三和李四的帳戶不在一個數據庫中甚至不在一個應用系統裏,實現轉帳事務須要經過遠程調用,因爲網絡問題就會致使分佈式事務問題。
跨JVM進程產生分佈式事務
典型的場景就是微服務架構:微服務之間經過遠程調用完成事務操做。好比:訂單微服務和庫存微服務,下單的同時訂單微服務請求庫存微服務減小庫存。
跨數據庫實例產生分佈式事務
單體系統訪問多個數據庫實例當單體系統須要訪問多個數據庫(實例)時就會產生分佈式事務。好比:用戶信息和訂單信息分別在兩個MySQL實例存儲,用戶管理系統刪除用戶信息,須要分別刪除用戶信息及用戶的訂單信息,因爲數據分佈在不一樣的數據實例,須要經過不一樣的數據庫連接去操做數據,此時產生分佈式事務。
多服務訪問同一個數據庫實例
訂單微服務和庫存微服務即便訪問同一個數據庫也會產生分佈式事務,緣由就是跨JVM進程,兩個微服務持有了不一樣的數據庫連接進行數據庫操做,此時產生分佈式事務。
經過前面的學習,咱們瞭解到了分佈式事務的基礎概念。與本地事務不一樣的是,分佈式系統之因此叫分佈式,是由於提供服務的各個節點分佈在不一樣機器上,相互之間經過網絡交互。不能由於有一點網絡問題就致使整個系統沒法提供服務,網絡因素成爲了分佈式事務的考量標準之一。所以,分佈式事務須要更進一步的理論支持,接下來,咱們先來學習一下分佈式事務的CAP理論。
CAP 是 Consistency、Availability、Partition tolerance 三個單詞的縮寫,分別表示一致性、可用性、分區容忍性。
下面爲了方便對CAP理論的理解,咱們結合電商系統中的一些業務場景來理解CAP。
以下圖,是商品信息管理的執行流程:
總體執行流程以下
商品服務請求主數據庫寫入商品信息(添加商品、修改商品、刪除商品)
主數據庫向商品服務響應寫入成功
商品服務請求從數據庫讀取商品信息
一致性是指寫操做後的讀操做能夠讀取到最新的數據狀態,當數據分佈在多個節點上,從任意結點讀取到的數據都是最新的狀態。
上圖中,商品信息的讀寫要知足一致性就是要實現以下目標:
商品服務寫入主數據庫成功,則向從數據庫查詢新數據也成功。
商品服務寫入主數據庫失敗,則向從數據庫查詢新數據也失敗。
如何實現一致性?
寫入主數據庫後要將數據同步到從數據庫。
寫入主數據庫後,在向從數據庫同步期間要將從數據庫鎖定,待同步完成後再釋放鎖,以避免在新數據寫入成功後,向從數據庫查詢到舊的數據。
分佈式系統一致性的特色:
因爲存在數據同步的過程,寫操做的響應會有必定的延遲。
爲了保證數據一致性會對資源暫時鎖定,待數據同步完成釋放鎖定資源。
若是請求數據同步失敗的結點則會返回錯誤信息,必定不會返回舊數據。
可用性是指任何事務操做均可以獲得響應結果,且不會出現響應超時或響應錯誤。
上圖中,商品信息讀取知足可用性就是要實現以下目標:
從數據庫接收到數據查詢的請求則當即可以響應數據查詢結果。
從數據庫不容許出現響應超時或響應錯誤。
如何實現可用性
寫入主數據庫後要將數據同步到從數據庫。
因爲要保證從數據庫的可用性,不可將從數據庫中的資源進行鎖定。
即時數據尚未同步過來,從數據庫也要返回要查詢的數據,哪怕是舊數據,若是連舊數據也沒有則能夠按照約定返回一個默認信息,但不能返回錯誤或響應超時。
分佈式系統可用性的特色:全部請求都有響應,且不會出現響應超時或響應錯誤
一般分佈式系統的各各結點部署在不一樣的子網,這就是網絡分區,不可避免的會出現因爲網絡問題而致使結點之間通訊失敗,此時仍可對外提供服務,這叫分區容忍性。
上圖中,商品信息讀寫知足分區容忍性就是要實現以下目標:
主數據庫向從數據庫同步數據失敗不影響讀寫操做。
其一個結點掛掉不影響另外一個結點對外提供服務。
如何實現分區容忍性?
儘可能使用異步取代同步操做,例如使用異步方式將數據從主數據庫同步到從數據,這樣結點之間能有效的實現鬆耦合。
添加從數據庫結點,其中一個從結點掛掉其它從結點提供服務。
分佈式分區容忍性的特色:分區容忍性分是布式系統具有的基本能力
上邊商品管理的例子是否同時具有 CAP 呢?
在全部分佈式事務場景中不會同時具有 CAP 三個特性,由於在具有了P的前提下C和A是不能共存的
好比,下圖知足了P即表示實現分區容忍:
本圖分區容忍的含義是:
主數據庫經過網絡向從數據庫同步數據,能夠認爲主從數據庫部署在不一樣的分區,經過網絡進行交互。
當主數據庫和從數據庫之間的網絡出現問題不影響主數據庫和從數據庫對外提供服務。
其中一個節點掛掉不影響另外一個節點對外提供服務。
若是要實現 C 則必須保證數據一致性,在數據同步的時候爲防止向從數據庫查詢不一致的數據則須要將從數據庫數據鎖定,待同步完成後解鎖,若是同步失敗從數據庫要返回錯誤信息或超時信息。
若是要實現 A 則必須保證數據可用性,無論任什麼時候候均可以向從數據查詢數據,則不會響應超時或返回錯誤信息。經過分析發如今知足P的前提下 C 和 A 存在矛盾性。
因此在生產中對分佈式事務處理時要根據需求來肯定知足 CAP 的哪兩個方面。
AP
放棄一致性,追求分區容忍性和可用性。這是不少分佈式系統設計時的選擇。
例如:上邊的商品管理,徹底能夠實現 AP,前提是隻要用戶能夠接受所查詢到的數據在必定時間內不是最新的便可。
一般實現 AP 都會保證最終一致性,後面將的 BASE 理論就是根據 AP 來擴展的,一些業務場景好比:訂單退款,今日退款成功,明日帳戶到帳,只要用戶能夠接受在必定的時間內到帳便可。
CP
放棄可用性,追求一致性和分區容錯性,zookeeper 其實就是追求的強一致,又好比跨行轉帳,一次轉帳請求要等待雙方銀行系統都完成整個事務纔算完成。
CA
放棄分區容忍性,即不進行分區,不考慮因爲網絡不通或結點掛掉的問題,則能夠實現一致性和可用性。那麼系統將不是一個標準的分佈式系統,最經常使用的關係型數據就知足了 CA。
上邊的商品管理,若是要實現 CA 則架構以下:
主數據庫和從數據庫中間不在進行數據同步,數據庫能夠響應每次的查詢請求,經過事務隔離級別實現每一個查詢請求均可以返回最新的數據。
CAP 是一個已經被證明的理論,一個分佈式系統最多隻能同時知足:一致性(Consistency)、可用性(Availability)和分區容忍性(Partition tolerance)這三項中的兩項。它能夠做爲咱們進行架構設計、技術選型的考量標準。對於多數大型互聯網應用的場景,結點衆多、部署分散,並且如今的集羣規模愈來愈大,因此節點故障、網絡故障是常態,並且要保證服務可用性達到 N 個 9(99.99..%),並要達到良好的響應性能來提升用戶體驗,所以通常都會作出以下選擇:保證 P 和 A ,捨棄 C 強一致,保證最終一致性。
強一致性和最終一致性
CAP 理論告訴咱們一個分佈式系統最多隻能同時知足一致性(Consistency)、可用性(Availability)和分區容忍性(Partition tolerance)這三項中的兩項,其中AP在實際應用中較多,AP 即捨棄一致性,保證可用性和分區容忍性,可是在實際生產中不少場景都要實現一致性,好比前邊咱們舉的例子主數據庫向從數據庫同步數據,即便不要一致性,可是最終也要將數據同步成功來保證數據一致,這種一致性和 CAP 中的一致性不一樣,CAP 中的一致性要求 在任什麼時候間查詢每一個結點數據都必須一致,它強調的是強一致性,可是最終一致性是容許能夠在一段時間內每一個結點的數據不一致,可是通過一段時間每一個結點的數據必須一致,它強調的是最終數據的一致性。
Base 理論介紹
BASE 是 Basically Available(基本可用)、Soft state(軟狀態)和 Eventually consistent (最終一致性)三個短語的縮寫。BASE 理論是對 CAP 中 AP 的一個擴展,經過犧牲強一致性來得到可用性,當出現故障容許部分不可用但要保證核心功能可用,容許數據在一段時間內是不一致的,但最終達到一致狀態。知足BASE理論的事務,咱們稱之爲「柔性事務」。
基本可用:分佈式系統在出現故障時,容許損失部分可用功能,保證核心功能可用。如電商網站交易付款出現問題了,商品依然能夠正常瀏覽。
軟狀態:因爲不要求強一致性,因此BASE容許系統中存在中間狀態(也叫軟狀態),這個狀態不影響系統可用性,如訂單的"支付中"、「數據同步中」等狀態,待數據最終一致後狀態改成「成功」狀態。
最終一致:最終一致是指通過一段時間後,全部節點數據都將會達到一致。如訂單的"支付中"狀態,最終會變 爲「支付成功」或者"支付失敗",使訂單狀態與實際交易結果達成一致,但須要必定時間的延遲、等待。
前面學習了分佈式事務的基礎理論,以理論爲基礎,針對不一樣的分佈式場景業界常見的解決方案有 2PC、3PC、TCC、可靠消息最終一致性、最大努力通知這幾種。
2PC 即兩階段提交協議,是將整個事務流程分爲兩個階段,準備階段(Prepare phase)、提交階段(commit phase),2 是指兩個階段,P 是指準備階段,C 是指提交階段。
舉例:張三和李四很久不見,老友約起聚餐,飯店老闆要求先買單,才能出票。這時張三和李四分別抱怨近況不如意,囊中羞澀,都不肯意請客,這時只能AA。只有張三和李四都付款,老闆才能出票安排就餐。但因爲張三和李四都是鐵公雞,造成了尷尬的一幕:
準備階段:老闆要求張三付款,張三付款。老闆要求李四付款,李四付款。
提交階段:老闆出票,兩人拿票紛紛落座就餐。
例子中造成了一個事務,若張三或李四其中一人拒絕付款,或錢不夠,店老闆都不會給出票,而且會把已收款退回。
整個事務過程由事務管理器和參與者組成,店老闆就是事務管理器,張3、李四就是事務參與者,事務管理器負責決策整個分佈式事務的提交和回滾,事務參與者負責本身本地事務的提交和回滾。
在計算機中部分關係數據庫如 Oracle、MySQL 支持兩階段提交協議,以下圖:
準備階段(Prepare phase):事務管理器給每一個參與者發送 Prepare 消息,每一個數據庫參與者在本地執行事務,並寫本地的 Undo/Redo 日誌,此時事務沒有提交。(Undo 日誌是記錄修改前的數據,用於數據庫回滾,Redo 日誌是記錄修改後的數據,用於提交事務後寫入數據文件)
提交階段(commit phase):若是事務管理器收到了參與者的執行失敗或者超時消息時,直接給每一個參與者發送回滾(Rollback)消息;不然,發送提交(Commit)消息;參與者根據事務管理器的指令執行提交或者回滾操做,並釋放事務處理過程當中使用的鎖資源。注意:必須在最後階段釋放鎖資源。
下圖展現了2PC的兩個階段,分紅功和失敗兩個狀況說明:
成功狀況
失敗狀況
2PC的傳統方案是在數據庫層面實現的,如 Oracle、MySQL 都支持 2PC 協議,爲了統一標準減小行業內沒必要要的對接成本,須要制定標準化的處理模型及接口標準,國際開放標準組織 Open Group 定義了分佈式事務處理模型DTP(Distributed Transaction Processing Reference Model)。
爲了讓你們更明確 XA 方案的內容,下面以新用戶註冊送積分爲例來講明:
執行流程以下:
應用程序(AP)持有用戶庫和積分庫兩個數據源。
應用程序(AP)經過 TM 通知用戶庫 RM 新增用戶,同時通知積分庫RM爲該用戶新增積分,RM 此時並未提交事務,此時用戶和積分資源鎖定。
TM 收到執行回覆,只要有一方失敗則分別向其餘 RM 發起回滾事務,回滾完畢,資源鎖釋放。
TM 收到執行回覆,所有成功,此時向全部 RM 發起提交事務,提交完畢,資源鎖釋放。
DTP 模型定義以下角色:
AP(Application Program):即應用程序,能夠理解爲使用 DTP 分佈式事務的程序。
RM(Resource Manager):即資源管理器,能夠理解爲事務的參與者,通常狀況下是指一個數據庫實例,經過資源管理器對該數據庫進行控制,資源管理器控制着分支事務。
TM(Transaction Manager):事務管理器,負責協調和管理事務,事務管理器控制着全局事務,管理事務生命週期,並協調各個 RM。全局事務是指分佈式事務處理環境中,須要操做多個數據庫共同完成一個工做,這個工做便是一個全局事務。
DTP 模型定義TM和RM之間通信的接口規範叫 XA,簡單理解爲數據庫提供的 2PC 接口協議,基於數據庫的 XA 協議來實現 2PC 又稱爲 XA 方案
以上三個角色之間的交互方式以下:
TM 向 AP 提供 應用程序編程接口,AP 經過 TM 提交及回滾事務。
TM 交易中間件經過 XA 接口來通知 RM 數據庫事務的開始、結束以及提交、回滾等。
總結
整個 2PC 的事務流程涉及到三個角色 AP、RM、TM。AP 指的是使用 2PC 分佈式事務的應用程序;RM 指的是資源管理器,它控制着分支事務;TM 指的是事務管理器,它控制着整個全局事務。
(1)在準備階段 RM 執行實際的業務操做,但不提交事務,資源鎖定
(2)在提交階段 TM 會接受 RM 在準備階段的執行回覆,只要有任一個RM執行失敗,TM 會通知全部 RM 執行回滾操做,不然,TM 將會通知全部 RM 提交該事務。提交階段結束資源鎖釋放。
XA方案的問題
須要本地數據庫支持XA協議。
資源鎖須要等到兩個階段結束才釋放,性能較差。
Seata 是由阿里中間件團隊發起的開源項目 Fescar,後改名爲 Seata,它是一個是開源的分佈式事務框架。
傳統 2PC 的問題在 Seata 中獲得瞭解決,它經過對本地關係數據庫的分支事務的協調來驅動完成全局事務,是工做在應用層的中間件。主要優勢是性能較好,且不長時間佔用鏈接資源,它以高效而且對業務 0 侵入的方式解決微服務場景下面臨的分佈式事務問題,它目前提供 AT 模式(即 2PC)及 TCC 模式的分佈式事務解決方案。
Seata 的設計思想以下:
Seata 的設計目標其一是對業務無侵入,所以從業務無侵入的 2PC 方案着手,在傳統 2PC的基礎上演進,並解決 2PC 方案面臨的問題。
Seata 把一個分佈式事務理解成一個包含了若干分支事務的全局事務。全局事務的職責是協調其下管轄的分支事務達成一致,要麼一塊兒成功提交,要麼一塊兒失敗回滾。此外,一般分支事務自己就是一個關係數據庫的本地事務,下圖是全局事務與分支事務的關係圖:
與傳統 2PC 的模型相似,Seata 定義了 3 個組件來協議分佈式事務的處理過程:
Transaction Coordinator(TC):事務協調器,它是獨立的中間件,須要獨立部署運行,它維護全局事務的運行狀態,接收 TM 指令發起全局事務的提交與回滾,負責與 RM 通訊協調各各分支事務的提交或回滾。
Transaction Manager(TM):事務管理器,TM 須要嵌入應用程序中工做,它負責開啓一個全局事務,並最終向 TC 發起全局提交或全局回滾的指令。
Resource Manager(RM):控制分支事務,負責分支註冊、狀態彙報,並接收事務協調器 TC 的指令,驅動分支(本地)事務的提交和回滾。
還拿新用戶註冊送積分舉例Seata的分佈式事務過程:
具體的執行流程以下:
用戶服務的 TM 向 TC 申請開啓一個全局事務,全局事務建立成功並生成一個全局惟一的 XID。
用戶服務的 RM 向 TC 註冊分支事務,該分支事務在用戶服務執行新增用戶邏輯,並將其歸入 XID 對應全局事務的管轄。
用戶服務執行分支事務,向用戶表插入一條記錄。
邏輯執行到遠程調用積分服務時(XID 在微服務調用鏈路的上下文中傳播)。積分服務的 RM 向 TC 註冊分支事務,該分支事務執行增長積分的邏輯,並將其歸入 XID 對應全局事務的管轄。
積分服務執行分支事務,向積分記錄表插入一條記錄,執行完畢後,返回用戶服務。
用戶服務分支事務執行完畢。
TM 向 TC 發起針對 XID 的全局提交或回滾決議。
TC 調度 XID 下管轄的所有分支事務完成提交或回滾請求。
Seata實現2PC與傳統2PC的差異
架構層次方面:傳統 2PC 方案的 RM 其實是在數據庫層,RM 本質上就是數據庫自身,經過 XA 協議實現,而 Seata 的 RM 是以 jar 包的形式做爲中間件層部署在應用程序這一側的。
兩階段提交方面:傳統 2PC不管第二階段的決議是 commit 仍是 rollback ,事務性資源的鎖都要保持到 Phase2 完成才釋放。而 Seata 的作法是在 Phase1 就將本地事務提交,這樣就能夠省去 Phase2 持鎖的時間,總體提升效率。
本節講解了傳統 2PC(基於數據庫 XA 協議)和 Seata 實現 2PC 的兩種 2PC 方案,因爲 Seata 的 0 侵入性而且解決了傳統 2PC 長期鎖資源的問題,推薦採用 Seata 實現 2PC。
TCC 是 Try、Confirm、Cancel 三個詞語的縮寫,TCC 要求每一個分支事務實現三個操做:預處理 Try、確認 Confirm、撤銷 Cancel。Try 操做作業務檢查及資源預留,Confirm 作業務確認操做,Cancel 實現一個與 Try 相反的操做即回滾操做。TM 首先發起全部的分支事務的 Try 操做,任何一個分支事務的Try操做執行失敗,TM 將會發起全部分支事務的 Cancel 操做,若 Try 操做所有成功,TM 將會發起全部分支事務的 Confirm 操做,其中 Confirm/Cancel 操做若執行失敗,TM 會進行重試。
分支事務成功狀況:
分支事務失敗狀況:
TCC 分爲三個階段:
Try 階段是作完業務檢查(一致性)及資源預留(隔離),此階段僅是一個初步操做,它和後續的 Confirm 一塊兒才能真正構成一個完整的業務邏輯。
Confirm 階段是作確認提交,Try 階段全部分支事務執行成功後開始執行 Confirm。一般狀況下,採用 TCC 則認爲 Confirm 階段是不會出錯的。即:只要 Try 成功,Confirm 必定成功。若 Confirm 階段真的出錯了,需引入重試機制或人工處理。
Cancel 階段是在業務執行錯誤須要回滾的狀態下執行分支事務的業務取消,預留資源釋放。一般狀況下,採用 TCC 則認爲 Cancel 階段也是必定成功的。若 Cancel 階段真的出錯了,需引入重試機制或人工處理。
TM 事務管理器
TM事務管理器能夠實現爲獨立的服務,也可讓全局事務發起方充當 TM 的角色,TM 獨立出來是爲了成爲公 用組件,是爲了考慮系統結構和軟件複用。
TM 在發起全局事務時生成全局事務記錄,全局事務 ID 貫穿整個分佈式事務調用鏈條,用來記錄事務上下文, 追蹤和記錄狀態,因爲 Confirm 和 Cancel 失敗需進行重試,所以須要實現爲冪等,冪等性是指同一個操做不管請求多少次,其結果都相同。
TCC須要注意三種異常處理分別是空回滾、冪等、懸掛
空回滾
在沒有調用 TCC 資源 Try 方法的狀況下,調用了二階段的 Cancel 方法,Cancel 方法須要識別出這是一個空回滾,而後直接返回成功。
出現緣由是當一個分支事務所在服務宕機或網絡異常,分支事務調用記錄爲失敗,這個時候實際上是沒有執行 Try 階段,當故障恢復後,分佈式事務進行回滾則會調用二階段的 Cancel 方法,從而造成空回滾。
解決思路是關鍵就是要識別出這個空回滾。思路很簡單就是須要知道一階段是否執行,若是執行了,那就是正常回滾;若是沒執行,那就是空回滾。前面已經說過 TM 在發起全局事務時生成全局事務記錄,全局事務 ID 貫穿整個分佈式事務調用鏈條。再額外增長一張分支事務記錄表,其中有全局事務 ID 和分支事務 ID,第一階段 Try 方法裏會插入一條記錄,表示一階段執行了。Cancel 接口裏讀取該記錄,若是該記錄存在,則正常回滾;若是該記錄不存在,則是空回滾。
冪等
經過前面介紹已經瞭解到,爲了保證 TCC 二階段提交重試機制不會引起數據不一致,要求 TCC 的二階段 Try、Confirm 和 Cancel 接口保證冪等,這樣不會重複使用或者釋放資源。若是冪等控制沒有作好,頗有可能致使數據不一致等嚴重問題。
解決思路在上述"分支事務記錄"中增長執行狀態,每次執行前都查詢該狀態。
懸掛
懸掛就是對於一個分佈式事務,其二階段 Cancel 接口比 Try 接口先執行。
出現緣由是在 RPC 調用分支事務 Try 時,先註冊分支事務,再執行 RPC 調用,若是此時 RPC 調用的網絡發生擁堵,一般 RPC 調用是有超時時間的,RPC 超時之後,TM 就會通知 RM 回滾該分佈式事務,可能回滾完成後,RPC 請求才到達參與者真正執行,而一個 Try 方法預留的業務資源,只有該分佈式事務才能使用,該分佈式事務第一階段預留的業務資源就再也沒有人可以處理了,對於這種狀況,咱們就稱爲懸掛,即業務資源預留後無法繼續處理。
解決思路是若是二階段執行完成,那一階段就不能再繼續執行。在執行一階段事務時判斷在該全局事務下,"分支事務記錄"表中是否已經有二階段事務記錄,若是有則不執行 Try。
舉例,場景爲 A 轉帳 30 元給 B,A 和 B 帳戶在不一樣的服務。
方案
帳戶 A
try: 檢查餘額是否夠30元 扣減30元
confirm: 空
cancel: 增長30元
帳戶 B
try: 增長30元
confirm: 空
cancel: 減小30元
方案說明
(1)帳戶 A,這裏的餘額就是所謂的業務資源,按照前面提到的原則,在第一階段須要檢查並預留業務資源,所以,咱們在扣錢 TCC 資源的 Try 接口裏先檢查 A 帳戶餘額是否足夠,若是足夠則扣除 30 元。Confirm 接口表示正式提交,因爲業務資源已經在 Try 接口裏扣除掉了,那麼在第二階段的 Confirm 接口裏能夠什麼都不用作。Cancel 接口的執行表示整個事務回滾,帳戶A回滾則須要把 Try 接口裏扣除掉的 30 元還給帳戶。
(2)帳號B,在第一階段 Try 接口裏實現給帳戶 B 加錢,Cancel 接口的執行表示整個事務回滾,帳戶 B 回滾則須要把 Try 接口裏加的 30 元再減去。
方案問題分析
若是帳戶 A 的 Try 沒有執行在 Cancel 則就多加了 30 元。
因爲 Try、Cancel、Confirm 都是由單獨的線程去調用,且會出現重複調用,因此都須要實現冪等。
帳號 B 在 Try 中增長 30 元,當 Try 執行完成後可能會其它線程給消費了。
若是帳戶 B 的 Try 沒有執行在 Cancel 則就多減了 30 元。
問題解決
帳戶 A 的 Cancel 方法須要判斷 Try 方法是否執行,正常執行 Try 後方可執行 Cancel。
Try、Cancel、Confirm方法實現冪等。
帳號 B 在 Try 方法中不容許更新帳戶金額,在 Confirm 中更新帳戶金額。
帳戶 B 的 Cancel 方法須要判斷 Try 方法是否執行,正常執行 Try 後方可執行 Cancel。
優化方案
帳戶 A
try: try冪等校驗 try懸掛處理 檢查餘額是否夠30元 扣減30元
confirm: 空
cancel: cancel冪等校驗 cancel空回滾處理 增長可用餘額30元
帳戶 B
try: 空confirm: confirm冪等校驗 正式增長30元cancel: 空
若是拿 TCC 事務的處理流程與 2PC 兩階段提交作比較,2PC 一般都是在跨庫的 DB 層面,而 TCC 則在應用層面的處理,須要經過業務邏輯來實現。這種分佈式事務的實現方式的優點在於,可讓應用本身定義數據操做的粒度,使得下降鎖衝突、提升吞吐量成爲可能。
而不足之處則在於對應用的侵入性很是強,業務邏輯的每一個分支都須要實現 Try、Confirm、Cancel 三個操做。此外,其實現難度也比較大,須要按照網絡狀態、系統故障等不一樣的失敗緣由實現不一樣的回滾策略。
可靠消息最終一致性方案是指當事務發起方執行完成本地事務後併發出一條消息,事務參與方(消息消費者)必定可以接收消息並處理事務成功,此方案強調的是隻要消息發給事務參與方最終事務要達到一致。
此方案是利用消息中間件完成,以下圖:
事務發起方(消息生產方)將消息發給消息中間件,事務參與方從消息中間件接收消息,事務發起方和消息中間件之間,事務參與方(消息消費方)和消息中間件之間都是經過網絡通訊,因爲網絡通訊的不肯定性會致使分佈式事務問題。
所以可靠消息最終一致性方案要解決如下幾個問題:
本地事務與消息發送的原子性問題
本地事務與消息發送的原子性問題即:事務發起方在本地事務執行成功後消息必須發出去,不然就丟棄消息。即實現本地事務和消息發送的原子性,要麼都成功,要麼都失敗。本地事務與消息發送的原子性問題是實現可靠消息最終一致性方案的關鍵問題。
下面這種操做,先發送消息,在操做數據庫:
begin transaction; //1.發送MQ //2.數據庫操做commit transation;
這種狀況下沒法保證數據庫操做與發送消息的一致性,由於可能發送消息成功,數據庫操做失敗。那麼第二種方案,先進行數據庫操做,再發送消息:
begin transaction; //1.數據庫操做 //2.發送MQcommit transation;
這種狀況下貌似沒有問題,若是發送 MQ 消息失敗,就會拋出異常,致使數據庫事務回滾。但若是是超時異常,數據庫回滾,但 MQ 其實已經正常發送了,一樣會致使不一致。
事務參與方接收消息的可靠性
事務參與方必須可以從消息隊列接收到消息,若是接收消息失敗能夠重複接收消息。
消息重複消費的問題
因爲網絡2的存在,若某一個消費節點超時可是消費成功,此時消息中間件會重複投遞此消息,就致使了消息的重複消費。
要解決消息重複消費的問題就要實現事務參與方的方法冪等性。
上節討論了可靠消息最終一致性事務方案須要解決的問題,本節討論具體的解決方案。
本地消息表這個方案最初是 eBay 提出的,此方案的核心是經過本地事務保證數據業務操做和消息的一致性,而後經過定時任務將消息發送至消息中間件,待確認消息發送給消費方成功再將消息刪除。
下面以註冊送積分爲例來講明:下例共有兩個微服務交互,用戶服務和積分服務,用戶服務負責添加用戶,積分服務負責增長積分。
交互流程以下:
用戶註冊
用戶服務在本地事務新增用戶和增長 "積分消息日誌"。(用戶表和消息表經過本地事務保證一致)
begin transaction //1.新增用戶 //2.存儲積分消息日誌commit transation
這種狀況下,本地數據庫操做與存儲積分消息日誌處於同一個事務中,本地數據庫操做與記錄消息日誌操做具有原子性。
定時任務掃描日誌
如何保證將消息發送給消息隊列呢?
通過第一步消息已經寫到消息日誌表中,能夠啓動獨立的線程,定時對消息日誌表中的消息進行掃描併發送至消息中間件,在消息中間件反饋發送成功後刪除該消息日誌,不然等待定時任務下一週期重試。
消費消息
如何保證消費者必定能消費到消息呢?
這裏可使用 MQ 的 ack(即消息確認)機制,消費者監聽 MQ,若是消費者接收到消息而且業務處理完成後向 MQ 發送 ack(即消息確認),此時說明消費者正常消費消息完成,MQ 將再也不向消費者推送消息,不然消費者會不斷重試向消費者來發送消息。
積分服務接收到"增長積分"消息,開始增長積分,積分增長成功後向消息中間件迴應 ack,不然消息中間件將重複投遞此消息。
因爲消息會重複投遞,積分服務的"增長積分"功能須要實現冪等性。
可靠消息最終一致性就是保證消息從生產方通過消息中間件傳遞到消費方的一致性:
本地事務與消息發送的原子性問題。
事務參與方接收消息的可靠性。
可靠消息最終一致性事務適合執行週期長且實時性要求不高的場景。引入消息機制後,同步的事務操做變爲基於消息執行的異步操做, 避免了分佈式事務中的同步阻塞操做的影響,並實現了兩個服務的解耦。
[object Object][object Object]