分散式事務方案 - 最終一致性

路人甲Java發表於2019-05-17

在分散式時代,分庫分表是很常見的,微服務系統中,各個系統通常使用獨立的資料庫,所以,事務很難靠資料庫本身保證,只能靠業務系統來解決。

例如支付寶中的餘額寶、花唄,具體不清楚,但猜測應該就是2個服務,不是同一個資料庫,我們還花唄的時候通常都是從餘額寶中扣除的,這就是分散式事務,一個系統中扣減錢,一個系統中增加錢。

下面我們分析下最終一致性的實現方案,最終一致性通常都是使用訊息中介軟體來實現的,系統結構如下:

在這裡插入圖片描述
使用者向系統A發起轉賬請求,A先在自己的資料庫中扣錢,然後通過訊息中介軟體告訴B應該加錢,B收到後在自己的資料庫中加錢。

這裡有個關鍵問題,A更新資料庫和給訊息中介軟體發訊息是2個操作,如下兩個場景怎麼處理:

先更新資料庫,成功了,但傳送訊息失敗了,重發多次還是失敗

先發訊息,成功了,但資料庫更新失敗,訊息撤不回來了

都是因為這2個操作不是原子的,發做誰都有問題。

那看下這樣做是否可以,就是把更新資料庫和給訊息中介軟體發訊息放到一個事務中,這樣不就原子了嗎?

有問題,例如:

如果訊息傳送失敗,具體問題出在哪兒?是訊息中介軟體根本就沒收到訊息,還是收到訊息後response時出錯了?如果是根本沒收到還好一點,如果是收到了但響應失敗就麻煩了,導致A資料庫回滾,沒有扣錢,但B收到訊息了,加錢了。

如果發訊息時網路延遲很高怎麼辦,資料庫事務一直被拖著,效能差,風險高。

所以,放入一個事務中這種方法是不可取的。

為了保證原子性,可以變通一下,新增一個訊息表,A不直接往訊息中介軟體中發訊息,而是把訊息寫入訊息表,然後通過一個後臺程式不斷的把訊息寫入訊息中介軟體。

在這裡插入圖片描述

這個後臺程式源源不斷的把訊息表中的訊息發到訊息中介軟體,如果失敗就重試,可以保證:

訊息不會丟失

順序不亂

但會有訊息重複的情況,因為訊息傳送失敗可能是寫入失敗,也可能是寫入成功但響應失敗,所以訊息可能會重複,這個問題需要系統B來處理。

系統B需要考慮2個問題:

訊息丟失

B從訊息中介軟體中拿到訊息,還沒處理完就當機了,這條訊息怎麼辦?

需要通過ACK機制處理,消費成功的傳送ACK,對於沒有ACK的訊息,訊息中介軟體會再次推送。

訊息重複

ACK機制也存在訊息重複的情況,比如B已經處理完一條訊息,發ACK時失敗了,那麼這條訊息就還會被推過來。

還有就是上面說的後臺程式發訊息時可能重複。

對於重複訊息問題,可以加一個判重表,記錄處理成功的訊息,每次收到訊息時,先通過判重表判斷一下,如果重複了就不處理,實現冪等性。

這樣,整體結構就變為:

在這裡插入圖片描述

以上就是通過最終一致性解決分散式事務問題的基本思路,A 保證訊息不丟,B 保證訊息不漏、冪等。

對分散式事務有興趣、或有疑問的,可以加我微信itsoku交流

請關注公眾號javacode2018,免費領取年薪40萬的資料,更多好文及時推送給您

相關文章