架構成長之路:分佈式事務一致性解決方案總結!

一、從數據一致性談起

一致性問題,“萬惡之源”是數據冗餘和分佈並通過網絡交互+網絡異常是常態。

1、數據一致性的情形

  • 主庫、從庫和緩存數據一致性,相同數據冗餘,關係數據庫,為保證關據庫的高可用和高性能,一般會採用主從(備)架構並引入緩存。其中數據不一致性存在於數據冗餘的時間窗口內。常用的解決方案見數據庫之架構
  • 多副本數據之間的數據一致性,相同數據副本,大數據領域,一份數據會有多個副本並存儲到不同的節點上。客戶端可以訪問任何一個節點進行讀寫操作。常用的解決方案是基於Paxos、ZAB、Raft、Quorum、Gossip等的開源實現。這裡只是一提,暫不探討。感興趣可以自行谷歌或百度。
  • 分佈式服務之間的數據一致性,相關數據分佈,分佈式服務,不同的服務操作不同的庫(表),而且庫(表)間要保持一致。常用的解決方案是
    分佈式事務一致性解決方案。這也是本文要探討的內容。

2、數據一致性的概念

  • 強一致性
  • 弱一致性
  • 最終一致性

3、數據一致性的原理

  • ACID
  • CAP
  • BASE

4、數據一致性的協議

  • 兩階段提交協議
  • 三階段提交協議
  • TCC協議
  • Paxos協議
  • ZAB協議
  • Raft協議
  • Quorum協議
  • Gossip協議

二、分佈式服務間的數據一致性

架構成長之路:分佈式事務一致性解決方案總結!

所謂分佈式服務,就是把之前通過本地接口交互的模塊,拆分成單獨的應用獨立部署,並通過遠程接口和網絡消息交互。且不管這樣說嚴不嚴密,正不正確,理解就好。本文的重點也不是這個話題。簡單畫一張圖輔助理解,如圖。集中式架構,要想保證訂單表和庫存表的一致性,只要一個本地事務(ACID)就能保證兩者的強一致性。分佈式架構,訂單表由訂單服務操作,庫存表由庫存服務操作。要想保證訂單表和庫存表的一致性,那麼就必須保證訂單服務對訂單表的操作和庫存服務對庫存表的操作同事成功。之前的一個本地事務就變成了一個分佈式事務。由於服務之間通過網絡交互+網絡異常是常態,就會產生服務間數據不一致的情況。這就涉及一個分佈式事務一致性的問題。

三、分佈式事務一致性解決方案

1、接口同步調用模式與一致性解決方案

架構成長之路:分佈式事務一致性解決方案總結!

模式分析:A服務同步調用B服務的接口並等待結果返回,後續的流程會依賴B服務的返回結果。這種交互模式下,A服務得到的結果細分有三種情況。

  1. 請求發起階段網絡超時或異常,此時,B服務未收到請求,未作出相應的處理;
  2. 結果返回階段網絡超時或異常,此時,B服務已收到請求,並作出相應的處理;
  3. 正常結果返回(明確的成功或失敗)。

業務場景:適用於大規模、高併發的短小操作且依賴返回值的場景。例如,交易服務和庫存服務(卡券服務、紅包服務等)的交互、用戶登錄和准入服務的交互等。

解決方案:方案一,服務調用方查詢重試方案;方案二,TCC

方案。

:這兩種方案,保證數據一致性實際上還是靠“異步”,只不過需要快速校準,準實時。

1.服務調用方查詢重試方案,適合一個從業務服務場景。

架構成長之路:分佈式事務一致性解決方案總結!

  1. 查詢重試後依然失敗(極少),報警,人工處理或者準實時對賬系統自動校準;
  2. 重試次數不宜多,甚至只重試一次;
  3. B服務處理請求要做冪等。

2.TCC方案,適合多個從業務服務場景。TCC是阿里在二階段提交協議的基礎上提出的一種解決分佈式事務一致性的協議,原理圖如下。其對應的產品是DTX(老版是DTS)。DTS中有個快速開始的例子看明白了,TCC就基本OK了。在螞蟻金服內部被廣泛地應用於交易、轉賬、紅包等核心資金鍊路,服務於億級用戶的資金操作。

架構成長之路:分佈式事務一致性解決方案總結!

  1. 關於TCC,個人認為,理解原理很重要。工作中遇到吻合的場景可以根據原理自行實現,滿足業務即可;
  2. 一個開源實現:tcc-transaction

2、接口異步調用模式與一致性解決方案

架構成長之路:分佈式事務一致性解決方案總結!

模式分析:A服務調用B服務,B服務先受理請求並落庫,狀態是待處理。B服務處理請求很耗時,或者要依賴其他的服務。B服務處理完後通知A服務或者A服務定時去查詢B服務的處理結果。這種交互模式下,對於CASE-1,第1步和第2步同接口同步調用模式,第3步同消息異步處理模式;對於CASE-2,相當於兩次接口同步調用模式

業務場景:適用於非核心鏈路上負載較高的處理環節,這個環節經常耗時較長,並且對時效性要求不高。例如,用戶提現時,賬戶系統和提現系統的交互(CASE-1);提現系統和三方系統(銀行系統或者三方託管系統)的交互(CASE-2)。

解決方案服務被調方最大努力處理方案。由於B服務中請求有落庫,所以可以用定時任務不斷重試盡最大努力將請求處理出結果。處理後,將請求狀態設置成對應的結果落庫。然後再通知A服務或者A服務異步主動查詢。

架構成長之路:分佈式事務一致性解決方案總結!

  1. B服務通常都是接受請求並持久化後才返回A服務受理成功。避免服務進程被殺掉而導致請求丟失。
  2. 不管是第(1,2)兩步還是CASE-2中的第(3,4)兩步,如果查詢重試失敗,可以落庫,用定時任務處理,知道成功。反正不像接口同步調用模式
    ,A服務不需要實時的結果。

3、消息異步處理模式與一致性解決方案

架構成長之路:分佈式事務一致性解決方案總結!

模式分析:A服務將B服務需要的信息通過消息中間件傳遞給B服務,A服務無需知道B服務的處理結果。這種交互模式下,消息生產者要確保消息發送成功;消息消費者要確保消息消費成功。

業務場景:消息異步處理模式與接口異步調用模式類似,多應用於非核心鏈路上負載較高的處理環節中,井且服務的上游不關心下游的處理結果,下游也不需要向上遊返回處理結果。例如,在電商系統中,用戶下訂單支付且交易成功後,發送消息給物流系統或者賬務系統進行後續的處理。

解決方案生產者最大努力通知+消費者最大努力處理方案。

1.非事務消息,生產者先執行本地事務並將消息落庫,狀態標記為待發送,然後發送消息。如果發送成功,則將消息改為發送成功。定時任務定時從數據庫撈取在一定時間內待發送的消息並將消息發送。通過定時任務來保證消息的發送。為確保消息一定能消費,消費者一般採用手動ACK機制,那麼消息服務器必然會重發未ACK的消息,這就要求消息消費者做好冪等。

架構成長之路:分佈式事務一致性解決方案總結!

架構成長之路:分佈式事務一致性解決方案總結!

2.事務消息,以RocketMQ為例,下圖是RocketMQ事務消息的流程。官網有示例代碼。和不支持事務的消息中間相比,只是消息發送的時候,保證了和本地事務的一致。消費者實現還是不變。

架構成長之路:分佈式事務一致性解決方案總結!

  1. 定時任務重試發送消息和消息服務器重發未ACK的消息一般都是時間階梯式的(2n*時間間隔);
  2. 支持事務消息中間件之RocketMQ

四、保證操作冪等性的常用方法

  1. 有業務狀態,業務邏輯來保證冪等。比如接到支付成功的消息訂單狀態變成支付完成,如果當前狀態是支付完成,則再收到一個支付成功或者支付成功之前狀態的消息則說明消息重複了,不用再次處理。
  2. 無業務狀態,業務唯一ID保證冪等。增加一個去重表(或分佈式緩存)來記錄有業務唯一ID的操作。比如調用充值接口,當請求過來時,會根據唯一充值ID去查充值流水錶,若已經存在,則直接返回;否則繼續進行充值操作。

:保證冪等性的方法很多,根據具體的業務場景,總能找到保證冪等性的方法。

五、總結

  1. 接口同步調用模式,服務調用方查詢重試方案和TCC方案。
  2. 接口異步調用模式,服務被調方最大努力處理方案。
  3. 消息異步處理模式,生產者最大努力通知+消費者最大努力處理方案。
  4. 任何服務操作都需要提供一個查詢接口,用來向外部輸出操作執行的狀態。
  5. 永遠不要在本地事務中調用遠程服務,在這種場景下如果遠程服務出現了問題,則會拖長事務,導致應用服務器佔用太多的數據庫連接,讓服務器負載迅速攀升,在嚴重情況下會壓垮數據庫。
  6. 最後一道防線 - 對賬系統。
  7. 同步和異步的抉擇:
  • 可以異步的地方,就應該異步實現。如果業務邏輯允許,則我們可以將一些耗時較長的、用戶對響應沒有特別要求的操作異步化,以此來減少核心鏈路的層級,釋放系統的壓力。
  • 能用同步解決的問題,不要引入異步。如果性能不是問題,或者所處理的操作是短小的輕量級處理邏輯,那麼同步調用方式是最理想不過的,因為這樣不需要引入異步化的複雜處理流程。

注:如果,以上場景和解決方案,沒能包含您工作中遇到的場景,歡迎交流,並共同討論解決方案。


分享到:


相關文章: