使用訊息系統來解決分布式事務

2022-02-10 07:04:38 字數 1496 閱讀 6769

本篇文章綜合了網上的多篇部落格。

說到分布式事務,就會談到那個經典的」賬號轉賬」問題:2個賬號,分布處於2個不同的db,或者說2個不同的子系統裡面,a要扣錢,b要加錢,如何保證原子性?

一般的思路都是通過訊息中介軟體來實現「最終一致性」:a系統扣錢,然後發條訊息給中介軟體,b系統接收此訊息,進行加錢。

但這裡面有個問題:a是先update db,後傳送訊息呢? 還是先傳送訊息,後update db?

假設先update db成功,傳送訊息網路失敗,重發又失敗,怎麼辦?

假設先傳送訊息成功,update db失敗。訊息已經發出去了,又不能撤回,怎麼辦?

所以,這裡下個結論: 只要傳送訊息和update db這2個操作不是原子的,無論誰先誰後,都是有問題的。

那這個問題怎麼解決呢??

有人可能想到了,我可以把「傳送訊息」這個網路呼叫和update db放在同1個事務裡面,如果傳送訊息失敗,update db自動回滾。這樣不就保證2個操作的原子性了嗎?

這個方案看似正確,其實是錯誤的,原因有2:

(1)網路的2將軍問題:傳送訊息失敗,傳送方並不知道是訊息中介軟體真的沒有收到訊息呢?還是訊息已經收到了,只是返回response的時候失敗了?

如果是已經收到訊息了,而傳送端認為沒有收到,執行update db的回滾操作。則會導致a賬號的錢沒有扣,b賬號的錢卻加了。

(2)把網路呼叫放在db事務裡面,可能會因為網路的延時,導致db長事務。嚴重的,會block整個db。這個風險很大。

基於以上分析,我們知道,這個方案其實是錯誤的!

假設訊息中介軟體沒有提供「事務訊息」功能,比如你用的是kafka。那如何解決這個問題呢?

解決方案如下:

(1)producer端準備1張訊息表,把update db和insert message這2個操作,放在乙個db事務裡面。

(2)準備乙個後台程式,源源不斷的把訊息表中的message傳送給訊息中介軟體。失敗了,不斷重試重傳。允許訊息重複,但訊息不會丟,順序也不會打亂。

(3)consumer端準備乙個判重表。處理過的訊息,記在判重表裡面。實現業務的冪等。但這裡又涉及乙個原子性問題:如果保證訊息消費 + insert message到判重表這2個操作的原子性?

通過上面3步,我們基本就解決了這裡update db和傳送網路訊息這2個操作的原子性問題。

但這個方案的乙個缺點就是:需要設計db訊息表,同時還需要乙個後台任務,不斷掃瞄本地訊息。導致訊息的處理和業務邏輯耦合額外增加業務方的負擔。

rocketmq是乙個兩段式提交協議的實現,其提交過程如下:

總結:對比方案2和方案1,rocketmq最大的改變,其實就是把「掃瞄訊息表」這個事情,不讓業務方做,而是訊息中介軟體幫著做了。

至於訊息表,其實還是沒有省掉。因為訊息中介軟體要詢問傳送方,事物是否執行成功,還是需要乙個「變相的本地訊息表」,記錄事物執行狀態。

參考文件

解決分布式事務

1.分布式解決方案 2.非常詳細的乙個業務邏輯 至於消費端消費失敗?通過 返回訊息的狀態,過一段時間,rocketmq會繼續傳送 漲姿勢了 2段式 先查詢每個資料庫的狀態,ok了之後,第2段是本地執行然後提交事務。事務管理器 atomikos,jtom,bitromix 缺點是 一旦詢問的階段機器掛...

分布式系統中的分布式事務

分布式事務中可以借助mq訊息系統來進行事務控制,這一點與可靠訊息最終一致方案一樣。看來mq中介軟體確實在乙個分布式系統架構中,扮演者重要的角色。最大努力通知方案是比較簡單的分布式事務方案,它本質上就是通過定期校對,實現資料一致性。中介軟體如何保證訊息的一致性 問題的問法多種多樣,怎麼保證兩個伺服器的...

分布式 分布式事務

是資料庫執行過程中的乙個邏輯單位,由乙個有限的資料庫操作序列構成。事務的acid四大特性 原子性 atomicity 事務作為乙個整體被執行。一致性 consistency 從乙個一致的狀態轉換到另乙個一致的狀態。隔離性 isolation 多個事務併發執行時,併發事務之間互相影響的程度。永續性 d...