天天看點

分布式事物的幾種解決方案

分布式事務之兩階段送出協定(2PC)

兩階段送出協定(Two-phase Commit,2PC)經常被用來實作分布式事務。一般分為協調器C和若幹事務執行者Si兩種角色,這裡的事務執行者就是具體的資料庫,協調器可以和事務執行器在一台機器上。

分布式事物的幾種解決方案
  1. 我們的應用程式(client)發起一個開始請求到TC;
  2. TC先将<prepare>消息寫到本地日志,之後向所有的Si發起<prepare>消息。以支付寶轉賬到餘額寶為例,TC給A的prepare消息是通知支付寶資料庫相應賬目扣款1萬,TC給B的prepare消息是通知餘額寶資料庫相應賬目增加1w。為什麼在執行任務前需要先寫本地日志,主要是為了故障後恢複用,本地日志起到現實生活中憑證 的效果,如果沒有本地日志(憑證),出問題容易死無對證;
  3. Si收到<prepare>消息後,執行具體本機事務,但不會進行commit,如果成功傳回<yes>,不成功傳回<no>。同理,傳回前都應把要傳回的消息寫到日志裡,當作憑證。
  4. TC收集所有執行器傳回的消息,如果所有執行器都傳回yes,那麼給所有執行器發生送commit消息,執行器收到commit後執行本地事務的commit操作;如果有任一個執行器傳回no,那麼給所有執行器發送abort消息,執行器收到abort消息後執行事務abort操作。

注:TC或Si把發送或接收到的消息先寫到日志裡,主要是為了故障後恢複用。如某一Si從故障中恢複後,先檢查本機的日志,如果已收到<commit >,則送出,如果<abort >則復原。如果是<yes>,則再向TC詢問一下,确定下一步。如果什麼都沒有,則很可能在<prepare>階段Si就崩潰了,是以需要復原。

現如今實作基于兩階段送出的分布式事務也沒那麼困難了,如果使用Java,那麼可以使用開源軟體atomikos(http://www.atomikos.com/)來快速實作。

不過但凡使用過的上述兩階段送出的同學都可以發現性能實在是太差,根本不适合高并發的系統。為什麼?

  • 兩階段送出涉及多次節點間的網絡通信,通信時間太長!
  • 事務時間相對于變長了,鎖定的資源的時間也變長了,造成資源等待時間也增加好多!

TCC方案

TCC的全稱是:Try、Confirm、Cancel。

這個其實是用到了補償的概念,分為了三個階段:

1)Try階段:這個階段說的是對各個服務的資源做檢測以及對資源進行鎖定或者預留

2)Confirm階段:這個階段說的是在各個服務中執行實際的操作

3)Cancel階段:如果任何一個服務的業務方法執行出錯,那麼這裡就需要進行補償,就是執行已經執行成功的業務邏輯的復原操作

比如說跨銀行轉賬的時候,要涉及到兩個銀行的分布式事務,如果用TCC方案來實作,思路是這樣的:

分布式事物的幾種解決方案

1)Try階段:先把兩個銀行賬戶中的資金給它當機住就不讓操作了

2)Confirm階段:執行實際的轉賬操作,A銀行賬戶的資金扣減,B銀行賬戶的資金增加

3)Cancel階段:如果任何一個銀行的操作執行失敗,那麼就需要復原進行補償,就是比如A銀行賬戶如果已經扣減了,但是B銀行賬戶資金增加失敗了,那麼就得把A銀行賬戶資金給加回去

這種方案說實話幾乎很少用人使用,我們用的也比較少,但是也有使用的場景。因為這個事務復原實際上是嚴重依賴于你自己寫代碼來復原和補償了,會造成補償代碼巨大,非常之惡心。

一般來說跟錢相關的,跟錢打交道的,支付、交易相關的場景,會用TCC,嚴格保證分布式事務要麼全部成功,要麼全部自動復原,嚴格保證資金的正确性,在資金上出現問題。

比較适合的場景:這個就是除非你是真的一緻性要求太高,是你系統中核心之核心的場景,比如常見的就是資金類的場景,那你可以用TCC方案了,自己編寫大量的業務邏輯,自己判斷一個事務中的各個環節是否ok,不ok就執行補償/復原代碼。

而且最好是你的各個業務執行的時間都比較短。

但是說實話,一般盡量别這麼搞,自己手寫復原邏輯,或者是補償邏輯,實在太惡心了,業務代碼很難維護。

基于RocketMQ的方式

分布式事物的幾種解決方案

1.生産者發送halfMessage給RocketMQ。

2.RocketMQ确認收到halfMessage。

3.生産者執行本地事務。

4.若本地事務執行失敗,復原本地本地事務,并發送rollbackMessage給RocketMQ。

5.若本地事務執行成功,發送commitMessage消息給RocketMQ。

6.若RocketMQ長時間沒收到Rollback或者commit消息,會回調生産者的接口。

7.消費者消費消息(ack機制保證被消費)。

繼續閱讀