分布式事務--消息補償的最終一致


 

大規模業務數據的方案一般都是分庫分表,而且一些場景會同時跨多個庫發生業務。在 "分布式事務概述"一文中,我們講到事務消息的MQ補償方案是目前公認的較為理想的分布式事務解決方案,實施成本也較高,今天我們即講述這種補償方案的最終一致性落地細節。

 

一、消息補償流程

回顧之前我們提到,消息中間件在分布式系統中的主要作用:異步通訊、解耦、並發緩沖。基於MQ實現分布式事務一致性是一種異步確保型的實現方案,將同步阻塞的事務變成異步的,避免對數據庫事務的爭用。大致流程如下:

 

基於消息補償的分布式事務方案往往用在高並發場景下,將一個分布式事務拆成一個消息事務(A系統的本地操作+發消息)+B系統的本地操作,其中B系統的操作由消息驅動,只要消息事務成功,那么A操作一定成功,消息也一定發出來了,這時候B會收到消息去執行本地操作,如果本地操作失敗,消息會重投,直到B操作成功,這樣就變相地實現了A與B的分布式事務。

 

二、事務補償應用

2.1 假設業務場景

"假設用戶a從自己的余額中向用戶c余額中轉100元錢。用戶a余額存放在A庫中,用戶c余額存在C庫中"

 

 

由於分庫的存在,破壞了事務的原子性,如果沒有分布式事務,轉賬過程可能會出現如下的問題:

 

第1種情況,應用寫隊列超時導致重發了消息.那么結果是a本來向c轉賬100元.結果卻轉賬了200元..

第2種情況,應用將消息成功寫入隊列,但是隊列服務器掛了.結果是a向c轉賬失敗.

第3種情況,中間層(隊列的消費者)將消息取出,修改a的賬戶余額,但是用戶a的庫掛了,導致事務失敗.結果是a向c轉賬失敗.

第4種情況,中間層已經成功修改了用戶a的賬戶余額,但是在修改c用戶余額的時候,用戶c的數據庫掛了。結果是用戶a的錢扣了,但是用戶c的錢沒有增加.

第5種情況.中間層從隊列拿到了消息,但是還未及處理,中間層本身掛了..

 

2.2 基於隊列事務的最終一致性解決方法

需要的前置工具或表:

1. 分布式ID生成器

2. transaction_log(tran_id,a,c,money),事務業務日志表

3. message_log(tran_id,account,money),消息日志表

 

結合"消息補償流程"中的流程圖,總體過程如下:

1. 應用通過ID生成器生成事務id,將本次事務日志寫入事務業務日志表,暫不提交。

2. 向隊列發送兩個消息.一個消息是用戶a -100元,另一消息是用戶c +100元,兩個消息需要帶上第一步得到的tran_id。確保兩個消息都成功入隊列,則提交業務日志的事務。一旦有任何異常,回滾事務。提交了事務,應用則可以直接返回.提示用戶交易完成。

3. 中間層獲取消息。先連接用戶a的數據庫.查詢transaction_log表,如果沒有該全局事務ID,則不予處理.(確認有這個全局事務,才處理),查詢message_log表,如果存在記錄,則不予處理.(防止消息超時重發)。開始消費端本地事務.update用戶a余額,減100元.再寫message_log表,記錄本次處理,最后本地提交事務。

4. 中間層連接用戶c的數據庫,做相同的操作。

5. 一個定時任務,每隔5分鍾,檢查transaction_log和message_log中是否存在不一致的tran_id.如果有不一致的情況,則進行事務補償或人工處理。

6. 完成完成轉賬。

 

三、潛在問題

3.1 . 從上述流程來看,將本地事務和發消息放在了一個分布式事務里,需要保證要么本地操作成功並且對外發消息成功,要么兩者都失敗。實際中,支持這個原子操作的消息中間件並不多(rabbitMq、kafkaMq等都不支持),阿里開源的RocketMQ支持這一特性。

3.2. 隨着業務增多,transaction_log、message_log表會比較大,這2張表也需要考慮分庫。否則瓶頸會特別大。

3.3. 其他一些性能問題,如消費端檢查message_log時,怎樣防止消費重復消息等。
---------------------
作者:TY1972
來源:CSDN
原文:https://blog.csdn.net/meiliangdeng1990/article/details/81902301?utm_source=copy
版權聲明:本文為博主原創文章,轉載請附上博文鏈接!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM