1. 程式人生 > >分散式事務 ( DTS ) 你必須知道的事兒

分散式事務 ( DTS ) 你必須知道的事兒

目前雲端計算、大資料、網際網路領域的大部分系統都採用了SOA、微服務化的架構。一個涉及端到端全鏈路的業務操作往往會由多個服務和資料庫例項共同完成。因此,在一致性要求較高的業務場景中,如何保證多個服務之間RPC呼叫後的資料一致將成為關鍵點。

一、分散式系統/SOA/微服務架構的特點:

在大型分散式系統中要同時能夠滿足,分散式一致性(Consistency)、可用性(Availability)和分割槽容忍性(Partitiontolerance),是不存在的。在大多數情況下只能滿足其中的2項,而實現系統的最終一致性(Base理論)。
( 1 ) CAP特點:

a.一致性(Consistency):( 同樣資料在分散式系統的各個節點上都是一致的)

b.可用性(Availability):( 所有在分散式系統活躍的節點都能夠處理操作且能響應查詢)

c.分割槽容忍性(Partition Tolerance) :(如果出現了網路故障、一部分節點無法通訊,但是系統仍能夠工作)

( 2 ) ACID特點:

a.原子性(Atomicity)

一個事務(transaction)中的所有操作,要麼全部完成,要麼全部不完成,不會結束在中間某個環節。事務在執行過程中發生錯誤,會被回滾(Rollback)到事務開始前的狀態,就像這個事務從來沒有執行過一樣。

b.一致性(Consistency)

事務的一致性指的是在一個事務執行之前和執行之後資料庫都必須處於一致性狀態。如果事務成功地完成,那麼系統中所有變化將正確地應用,系統處於有效狀態。如果在事務中出現錯誤,那麼系統中的所有變化將自動地回滾,系統返回到原始狀態。

c.隔離性(Isolation)

指的是在併發環境中,當不同的事務同時操縱相同的資料時,每個事務都有各自的完整資料空間。由併發事務所做的修改必須與任何其他併發事務所做的修改隔離。事務檢視資料更新時,資料所處的狀態要麼是另一事務修改它之前的狀態,要麼是另一事務修改它之後的狀態,事務不會檢視到中間狀態的資料。

d.永續性(Durability)

指的是隻要事務成功結束,它對資料庫所做的更新就必須永久儲存下來。即使發生系統崩潰,重新啟動資料庫系統後,資料庫還能恢復到事務成功結束時的狀態。

二、分散式事務的基本介紹

分散式事務服務(Distributed Transaction Service

DTS)是一種分散式事務框架,用來確保在大規模分散式/微服務環境下端到端業務操作的最終一致性。

由CAP定理可知,任何大型的分散式系統/微服務在一致性、可用性和分割槽容忍這三點上只能保證其中的兩點。由於在分散式系統中經常發生丟包、網路故障,分割槽容忍性是必須要滿足的,同時為了兼顧高可用性,絕大部分系統都將強一致性需求轉化成最終一致性的需求,並通過冪等機制保證了資料的最終一致性。

三、常用的分散式技術介紹

( 1 ) 本地訊息表(經典的ebay模式)

該方案的核心思想在於分散式系統在處理任務時通過訊息日誌的方式來非同步執行。訊息日誌可以儲存至本地文字、資料庫或訊息佇列,然後再通過業務規則定時任務或人工自動重試。以線上支付系統的跨行轉賬為例:

第一步,虛擬碼如下,對使用者id為A的賬戶扣款1000元,通過本地事務將事務訊息(包括本地事務id、支付賬戶、收款賬戶、金額、狀態等)插入至訊息表:

Begin transaction

         update user_account set amount = amount - 1000 where userId = 'A'

         insert into trans_message(xid,payAccount,recAccount,amount,status) values(uuid(),'A','B',1000,1);

end transaction

commit;

第二步,通知對方使用者id為B,增加1000元,通常通過訊息MQ的方式傳送非同步訊息,對方訂閱並監聽訊息後自動觸發轉賬的操作;這裡為了保證冪等性,防止觸發重複的轉賬操作,需要在執行轉賬操作方新增一個trans_recv_log表用來做冪等,在第二階段收到訊息後,通過判斷trans_recv_log表來檢測相關記錄是否被執行,如果未被執行則會對B賬戶餘額執行加1000元的操作,並會將該記錄增加至trans_recv_log,事件結束後通過回撥更新trans_message的狀態值。
( 2 ) 訊息中介軟體

a.非事務訊息中介軟體

這裡仍然以上面跨行轉賬為例,我們很難保證在扣款完成之後對MQ投遞訊息的操作就一定能成功。這樣一致性似乎很難保證。以下虛擬碼說明了訊息投遞的異常:

try{

    boolean result = dao.update(model);//更新資料庫失敗丟擲異常

    if(result){

                      mq.send(model);//如果MQ超時或者接收方處理失敗,丟擲異常

    }

}catch(Exception ex){

                          rollback();//如果異常回滾

}

對於以上的執行情況主要有以下幾種:

操作資料庫成功,向MQ中投遞訊息也成功,該屬於正常情況,一切都OK。
操作資料庫失敗,不會向MQ中投遞訊息了。
操作資料庫成功,但是向MQ中投遞訊息時失敗,向外丟擲了異常,剛剛執行的更新資料庫的操作將被回滾。

從上面分析的幾種情況來看,基本上能確保,傳送訊息的可靠性。我們再來分析下消費者端的問題:

1.接收者取出訊息後,消費者對應的業務操作要執行成功。如果業務執行失敗,訊息不能失效或者丟失。需要保證訊息與業務操作一致。
2.儘量確保訊息的冪等性。如果出現重複訊息投遞,能夠進行冪等而不對業務產生影響。

b.支援事務的訊息中介軟體

Apache開源的RocketMQ中介軟體能夠支援一種事務訊息機制,確保本地操作和傳送訊息的非同步處理達到本地事務的結果一致。

第一階段,RocketMQ在執行本地事務之前,會先發送一個Prepared訊息,並且會持有這個訊息的介面回查地址。

第二階段,執行本地事物操作。

第三階段,確認訊息傳送,通過第一階段拿到的介面地址URL執行回查,並修改狀態,如果本地事務成功,則修改狀態為已提交,否則修改狀態為已回滾。
這裡寫圖片描述

其中,如果第三階段的確認訊息傳送失敗後,RocketMQ會有定時任務掃描叢集中的事務訊息,如果發現還是處於prepare狀態的訊息,它會向訊息傳送者確認本地事務是否已執行成功。RocketMQ會根據傳送端設定的策略來決定是回滾還是繼續傳送確認訊息。這樣就保證了訊息的傳送與本地事務同時成功或同時失敗。

再回到上面轉賬的例子,如果使用者A的賬戶餘額已經減少,且訊息已經發送成功,作為消費者使用者B開始消費這條訊息,這個時候就會出現消費失敗和消費超時兩個問題,解決超時問題的思路就是一直重試,直到消費端消費訊息成功,整個過程中有可能會出現訊息重複的問題,就需要採用前面說的冪等方案來進行處理。

分散式事務—2PC協議

為了解決大型分散式/微服務系統中的一致性問題,業界比較流行的做法是採用比較著名的有二階提交協議(2 Phase Commitment Protocol)和三階提交協議(3 PhaseCommitment Protocol)。考慮到效能問題,三階段提交協議目前較少被採用。本文也主要介紹二階段協議。

2PC協議

二階段提交協議是分散式系統中較為經典的處理資料一致性的解決方案。在大型的叢集環境中,對於單體微服務本身而言雖然能夠通過程式碼質量、Mock測試等方法來確保自身服務的可用性,但是無法能夠保證其他服務的可用性。當一個全鏈路的端到端業務操作,常常會跨多個節點、多個應用,為了能夠保證全域性事務的ACID特性,需要引入一個協調元件(這裡稱之為TM)來控制所有服務參與者(這裡稱之為RM)的操作結果,根據所有參與者的反饋結果來決定整個分散式事務究竟是提交還是回滾的結果。

第一階段:稱為準備(prepare)階段。事務協調者向各個服務應用傳送prepare請求,服務應用在得到請求後做預處理操作,預處理可能是做預檢查,也可能是把請求臨時儲存,可以理解為是一種試探性地提交。下面是一般的步驟:

a.事務協調者會問所有的參與者服務,是否可以提交操作。

b.各個參與者開始事務執行的準備工作:如資源上鎖,預留資源,寫回滾/重試的log。

c.參與者響應協調者,如果事務準備工作成功,則迴應“可以提交”,否則迴應拒絕提交。

第二階段:稱為提交(commit)/回滾(rollback)階段。是指事務真正提交或者回滾的階段。如果事務協調者發現事務參與者有一個在prepare階段出現失敗,則會要求所有的參與者進行回滾。如果協調者發現所有的參與者都prepare操作都是成功,那麼他將向所有的參與者發出提交請求,這時所有參與者才會正式提交。由此保證了要求全部提交成功,要麼全部失敗。下面是具體步驟:

a.如果所有的參與者都回應“可以提交”,那麼協調者向所有參與者傳送“正式提交”的命令。參與者完成正式提交,並釋放所有資源,然後迴應“完成”,協調者收集各個服務的“完成”迴應後結束事務。

b.如果有一個參與者迴應“拒絕提交”,那麼協調者向所有的參與者傳送“回滾操作”,並釋放所有的資源,然後迴應“回滾完成”,協調者收集各個服務應用的“回滾”返回後,取消整體的分散式事務。

下圖為二階段的成功和失敗示例圖:
這裡寫圖片描述

二階段提交協議解決的是分散式系統/微服務架構中資料強一致性的問題,其原理簡單,但缺點也是存在,主要缺點如下:

a.單點問題:協調者在整個二階段中的作用非常重要,一旦部署協調者元件服務的節點出現不可用宕機情況,那麼會影響整個分散式系統的正常執行。

b.同步阻塞:二階段提交執行過程中,所有服務參與者需要服從協調者的統一排程,期間處於阻塞狀態,會一定程度上影響整個系統的效率。