前言
分散式系統由於機器宕機、網路異常、訊息丟失、訊息亂序、資料錯誤、不可靠的 TCP、儲存資料丟失等原因面臨一系列挑戰,本文重點講述分散式系統面臨的挑戰之一資料一致性問題。
隨著計算能力的提升、網際網路的興起、資料的分佈和儲存需求、容錯性和可用性的要求、業務的分佈和協同需求以及雲端計算和大資料技術的發展,分散式系統變得越來越重要,並在各個領域得到廣泛應用。
分散式系統由於機器宕機、網路異常、訊息丟失、訊息亂序、資料錯誤、不可靠的 TCP、儲存資料丟失等原因面臨一系列挑戰,本文重點講述分散式系統面臨的挑戰之一資料一致性問題。
正文
本地事務
ACID:資料庫事務的幾個特性:原子性 (Atomicity)、一致性 ( Consistency )、隔離性 ( Isolation) 和永續性 (Durabilily)
原子性:一系列的操作整體不可拆分,要麼同時成功,要麼同時失敗
一致性:事務在開始前和結束後,資料庫的完整性約束沒有被破壞
隔離性:事務的執行是相互獨立的,它們不會相互干擾,一個事務不會看到另一個正在執行過程中的事務的資料
永續性:一個事務完成之後,事務的執行結果必須是落盤在資料庫持久化
分散式基本理論
1. CAP 定理
CAP是指一致性(Consistency)、可用性(Availability)和分割槽容忍性(Partition Tolerance)三個屬性,它們是分散式系統設計的重要因素。
● C 一致性
在分散式系統中的所有資料結點,在同一時刻是否同樣的值。如果在某個節點更新了資料,那麼在其他節點如果都能讀取到這個最新的資料,那麼就稱為強一致,如果有某個節點沒有讀取到,那就是分散式不一致。
● A 可用性
在叢集中一部分節點故障後,叢集整體是否還能響應客戶端的讀寫請求。
在現代的網際網路應用中,如果因為伺服器宕機等問題,導致服務長期不可用,是不可接受的。
● P 分割槽容錯性
系統能夠在節點之間發生網路分割槽(Partition)的情況下仍然能夠正常執行。
在分散式系統中,網路無法100%可靠,分割槽其實是一個必然現象,如果選擇CA而放棄P,那麼當發生分割槽現象時,爲了保證一致性,這個時候必須拒絕請求,這樣就不滿足A。所以分散式系統理論上不可能選擇CA架構,只能選擇CP或者AP架構。
提高分割槽容忍性的辦法就是一份資料複製到多個節點上,那麼出現分割槽之後,這份資料仍然能在其他區中讀取,容忍性就提高了。然而,把資料複製到多個節點,就會帶來一致性的問題,就是多個節點上面的資料可能是不一致的。
2. BASE理論
根據CAP定理,如果要完整的實現事務的ACID特性,只能放棄可用性選擇一致性,然而可用性在現在網際網路環境至關重要。
BASE 理論是對 CAP 中一致性和可用性權衡的結果,是CAP中AP的一個擴充套件。其核心思想是:強一致性無法得到保障時,根據具體的業務場景,採用適當的方式來達到最終一致性。
BASE 是 Basically Available (基本可用)、Soft state (軟狀態) 和 Eventually consistent (最終一致性) 三個短語的縮寫。
- BA:基本可用性,分散式系統在面對故障或分割槽的情況下,仍然能夠保證基本的可用性。即系統可以繼續執行並提供核心的功能,而不是完全崩潰。
- S:軟狀態,分散式系統中的資料狀態不需要實時保持一致,而是允許一段時間的資料不一致。資料狀態可以是中間狀態,可以根據系統自身的需要而變化,這種狀態允許一定的延遲和不一致性。
- E:最終一致性,經過一段時間後資料最終會達到一致狀態,但不要求實時的一致性。
3. 一致性模型
強一致性
當更新操作完成之後,任何多個後續程序、執行緒的訪問都會返回最新的更新過的值。
弱一致性
系統並不保證程序、執行緒的訪問都會返回最新的值。系統在資料寫入成功之後,不承諾立即可以讀到最新寫入的值,也不會承諾多久之後可以讀到。但會盡可能保證在某個時間級別之後,可以讓資料達到一致性狀態。
最終一致性
最終一致性也是弱一致性的一種,它無法保證資料更新後,所有讀操作都能看到最新數值,而是需要一個時間,在這個時間之後可以保證這一點,而在這個時間內,資料也許是不一致的,這個系統無法保證強一致性的時間片段被稱為不一致視窗。
不一致視窗的時間長短取決於很多因素,比如:備份資料的個數、網路傳輸延遲速度、系統負載等。
分散式事務方案
2PC
二階段提交協議(Two-phase commit protocol),簡稱 2PC。兩階段提交是一種強一致性事務協議,它分爲準備階段和提交階段。有熟悉 MySQL 的同學可能馬上就能想到,MySQL 的事務提交就是透過幾種日誌來實現二階段提交的。
1. 2PC流程
以下是2PC協議的流程:
● 準備階段(Prepare Phase):
○ 協調者向所有參與者詢問是否可以提交事務,同步等待各參與者的響應。
○ 參與者執行本地事務操作,並將Undo資訊和Redo資訊寫入日誌。
○ 各參與者響應協調者發起的詢問。如果參與者的事務執行成功,則返回YES訊號;如果參與者的事務執行失敗,則返回NO訊號。
● 提交階段(Commit Phase):
1) 成功:當協調者從所有參與者獲得的響應都為YES時:
○ 協調者向所有參與者發出Commit請求。
○ 參與者正式完成操作,釋放在整個事務期間內佔用的資源,參與者向協調者傳送Committed訊息。
○ 協調者收到所有參與者反饋的Committed訊息後,完成事務。
2) 失敗:如果任一參與者在第一階段返回NO訊號,或者協調者在第一階段響應超時:
○ 協調者向所有參與者發出Rollback請求。
○ 參與者利用寫入的Undo資訊回滾本地事務,釋放各自佔用的資源,參與者向協調者傳送Rollbacked訊息。
○ 協調者收到所有參與者反饋的Rollbacked訊息後,取消事務。
2PC優點
原子性保證: 2PC 協議可以保證所有參與者要麼全部提交成功,要麼全部失敗回滾,從而實現跨多個分散式節點的事務的原子性。
簡單直觀: 2PC 的設計思路簡單,邏輯清晰,容易理解,這使得它在很多傳統的資料庫和分散式系統中得到了廣泛的應用,比如 MySQL 從 5.5 版本開始支援。
2PC缺點
同步阻塞: 在 2PC 的第一階段,所有參與者在響應協調者的準備請求後,必須等待最終的提交或回滾指令。這期間,所有參與者都處於阻塞狀態,無法進行其他操作,導致資源鎖定時間較長,在高併發場景下很明顯不太適用。
單點故障: 如果協調者在第二階段崩潰,參與者可能會無限期地等待指令,因為它們不知道應該提交還是回滾。這使得整個系統容易受到單點故障的影響。
資料不一致: 如果在第二階段中協調者向某些參與者傳送了提交指令,而其他參與者因為網路問題沒有收到指令,那麼這些沒有收到指令的參與者可能會選擇回滾,導致資料不一致。
複雜的恢復機制: 當系統崩潰後,恢復過程非常複雜,所有參與者必須保持足夠的資訊以便在系統恢復後能夠繼續完成 2PC 協議。
2. 3PC
三階段提交(Three-phase commit),是二階段提交(2PC)的改進版本。不同的是,三階段提交有兩個改進點,有效解決長時間阻塞和協調者單點故障。
引入超時機制
同時在協調者和參與者中都引入超時機制。
把準備階段拆分為兩個階段
3PC流程
準備階段的操作很重,一旦協調者發出開始準備的訊息,每個參與者都會立即寫重做日誌,涉及的資料資源都會被鎖住,因此將其一分為二。
如果此時某個參與者無法完成提交,所有參與者都做了無用功。所以,增加一輪詢問階段,這個階段參與者並不真正獲取鎖和佔用資源,只是對自身事務狀態的檢查,檢視是否具備執行事務的條件。
3PC協議的基本工作流程:
● 準備階段(Prepare Phase):
○ 協調者向所有參與者傳送準備請求,並等待參與者的響應。
○ 參與者接收到準備請求後,執行本地事務操作,並將準備就緒狀態(Prepare Ready)或中止狀態(Abort)的響應傳送給協調者。
● 預提交階段(Precommit Phase):
○ 協調者根據參與者的響應情況,判斷是否所有參與者都準備就緒。
○ 如果所有參與者都準備就緒,協調者向所有參與者傳送預提交請求,並等待參與者的響應。
○ 參與者接收到預提交請求後,執行事務的預提交操作,並將預提交完成狀態(Precommit)或中止狀態(Abort)的響應傳送給協調者。
● 提交階段(Commit Phase):
○ 協調者根據參與者的響應情況,判斷是否所有參與者都預提交成功。
○ 如果所有參與者都預提交成功,協調者向所有參與者傳送提交請求,並等待參與者的響應。
○ 參與者接收到提交請求後,執行事務的最終提交操作,並將提交完成狀態(Commit)或中止狀態(Abort)的響應傳送給協調者;
3PC優點
3PC可以解決單點故障問題,並減少阻塞。一旦參與者無法及時收到來自協調者的資訊之後,會預設執行commit,不會一直持有事務資源並處於阻塞狀態。
3PC缺點
雖然 3PC 提供了比 2PC 更好的容錯性和減少了阻塞的時間,但它仍然有一些缺點:
複雜性:3PC 比 2PC 更復雜,需要更多的訊息交換和更多的狀態管理。
效能開銷:3PC 引入了額外的階段和網路通訊,可能會導致更大的效能開銷。
極端情況:即使是 3PC,在某些極端的網路分割槽或多點故障情況下也可能無法保證事務的正確性。
因此,在實際應用中,需要權衡 3PC 帶來的好處與其複雜性和效能開銷之間的關係,確保它適合特定的業務場景和系統需求。
3PC應用場景
3PC通常用於需要較高可靠性的分散式系統中,尤其是在那些不能接受長時間鎖定資源的場景。例如:
分散式資料庫系統: 分散式資料庫可能使用 3PC 來確保跨多個數據中心的事務一致性。例如,一個全球性的銀行可能需要在不同國家的分支機構之間處理賬戶轉賬,這時3PC可以減少在網路延遲或某個分支機構失去響應時的影響。
電信網路: 在電信運營商的計費系統中,可能會使用 3PC 來同步跨多個服務點的賬單資訊,這些系統通常要求高可用性和快速響應,因此不能長時間阻塞。
大型分散式系統: 對於需要跨多個服務和元件協調工作的大型分散式系統,比如雲計算平臺,3PC可以在保持事務一致性的同時,減少參與者等待協調者指令的時間。
3. XA
XA(extended Architecture)是一種分散式事務處理的標準協議,用於確保多個資源管理器(Resource Manager)之間的事務一致性。它提供了在分佈式環境中同時提交或回滾多個資源的機制。
目前一些 關係型資料庫和訊息佇列 有支援XA協議,XA通常指基於資源層的 底層分散式事務 解決方案。分散式事務處理(Distributed Transaction Processing,DTP)模型定義了一個標準化的分散式事務處理的體系結構以及互動介面,DTP 規範中主要包含了 AP、RM、TM 三個部分,如下圖所示:
XA流程
元件概念:
● AP:應用程式(application program),事務的發起者,指定了構成全域性事務的相關資料訪問操作。
● RM:資源管理器(resource manager),事務的參與者,管理事務處理過程中涉及到的各種資源,如資料庫、訊息佇列等,當發生故障後,資源管理器可以將資料資源恢復到一致狀態。
● TM:事務管理器(transaction manager),事務的協調者,負責管理分散式事務的整個生命週期,包括事務的提交、回滾和恢復等。
XA約定了TM和RM之間雙向通訊的接口規範,並實現了二階段提交協議,從而在多個數據庫資源下保證 ACID 四個特性。
DTP模型可以理解為:應用程式訪問、使用RM的資源,並透過TM的事務介面(TX interface)定義需要執行的事務操作,然後TM和RM會基於XA規範,執行二階段提交協議進行事務的提交/回滾:
● 事務準備階段(Transaction Prepare Phase):
TM向所有RM傳送事務開始請求,開啟全域性事務。RM接收到事務開始請求後,執行本地事務操作,將事務的執行狀態通知TM。
● 事務提交/回滾階段(Transaction Commit/Rollback Phase):
如果所有RM的事務都成功執行,TM向所有RM傳送事務提交請求。RM接收到事務提交請求後,將事務結果持久化,並通知TM提交完成;如果任何一個RM的事務執行失敗,TM向所有RM傳送事務回滾請求。RM接收到事務回滾請求後,將事務回滾,並通知TM回滾完成;
XA優點
● 簡單易理解。
● 開發較容易,回滾之類的操作,由底層資料庫自動完成。
XA缺點
● 需要資源支援XA協議,非關係型資料庫大多不支援。
● 基於兩階段提交協議,在提交事務時需要多節點協調,客觀上延長了事務的執行時間,導致事務衝突、資源死鎖的機率高,不適合高併發的業務。
● 由於網路故障和參與者故障的存在,XA事務在故障發生時存在一段時間不一致狀態。
4. TCC
TCC(Try-Confirm-Cancel)是一種 應用層 的分散式事務解決方案,本質上屬於 補償性事務模式,它將事務分為三個步驟:嘗試(Try)、確認(Confirm)和取消(Cancel) :
TCC流程
TCC模式將一個分散式事務拆分為三個階段:
Try(嘗試階段)
在Try階段,事務發起方進行資源檢查和預留,預留好事務需要用到的所有業務資源。
Try 階段可能會重複執行,因此需要滿足冪等性,同時需要需要支援防懸掛控制,比如:Try超時,Cancel先到,Try後到場景,需要Cancel記錄事務id,Try對於該id拒絕執行;
Confirm(確認階段)
如果所有參與者在Try階段都執行成功,事務發起方會發送確認請求,要求各個參與者執行真正的提交操作。
Confirm 階段可能會重複執行,因此需要滿足冪等性。
Cancel(取消階段)
如果任何一個參與者在Try階段執行失敗,或者Confirm階段執行失敗,事務發起方會發送取消請求,要求各個參與者執行回滾操作,撤銷Try階段的操作。
Cancel階段可能會重複執行,因此需要滿足冪等性;同時允許空回滾,比如Try訊息丟失,需要Cancel請求時返回成功。
TCC優點
● 效能提升:具體業務來實現控制資源鎖的粒度變小,不會鎖定整個資源。
● 資料最終一致性:基於 Confirm 和 Cancel 的冪等性,保證事務最終完成確認或者取消,保證資料的一致性。
● 可靠性:解決了 XA 協議的協調者單點故障問題,由主業務方發起並控制整個業務活動,業務活動管理器也變成多點,引入叢集。
TCC缺點
● 有程式碼侵入:TCC的Try、Confirm和Cancel操作功能要按具體業務來實現,業務耦合度較高,提高了開發成本。
● 軟狀態:事務是最終一致的。
● 冪等性:需要考慮Confirm和Cancel的失敗情況,做好冪等處理。
TCC適用場景
TCC模式適用於需要跨多個服務進行分散式事務處理的場景。
5. SAGA
Saga 是一種 長事務 解決方案,它將一個大的分散式事務拆分成多個較小的事務片段,這些本地事務透過非同步訊息傳遞串聯起來,由Saga事務協調器協調,每個事務片段都有自己的補償操作。Saga模式的關鍵特點包括:
● 分散式事務拆分:Saga將大型事務拆分為多個小的事務片段,每個片段都可以獨立執行,並具有自己的本地事務;
● 補償操作:如果某個事務片段失敗,Saga會觸發相應的補償操作,以回滾或撤銷已執行的操作,以維持整個事務的一致性。
SAGA流程
Saga每個片段都會執行一些操作,如果所有片段都成功完成,則事務被提交。如果某個片段失敗,則會觸發相應的補償操作,恢復策略分為向前恢復和向後恢復兩種,以保持整個事務的一致性。
每個本地事務執行成功後,會發送訊息觸發下一個事務的執行。如果某個本地事務失敗,Saga 會執行一系列補償操作(回滾之前的操作)來保持資料的一致性。
向前恢復(forward recovery)
如果 Ti 事務提交失敗,則一直對 Ti 及 Ti 之後的進行重試,直至成功為止。這種恢復方式不需要補償,適用於事務最終都要成功的場景,如上面的圖例,子事務按照從左到右的順序執行,T1執行完畢以後T2 執行,然後是T3、T4、T5。
向後恢復(backward recovery)
如果 Ti 事務提交失敗,則一直執行 Ci 對 Ti 進行補償,直至成功為止(最大努力交付)。這裏要求 Ci 必須(在持續重試後)執行成功。向後恢復的執行模式為:T1,T2,…,Ti(失敗),Ci(補償),…,C2,C1。
通常來說我們把這種Saga執行事務的順序稱為個Saga的協調邏輯。這種協調邏輯有兩種模式,編排(Choreography)和控制(Orchestration)分別如下:
事務編排(Choreography)
參與者(子事務)之間的呼叫、分配、決策和排序,透過訊息傳遞完成,是一種 去中心化 的模式。參與者之間透過訊息傳遞機制串聯起來,透過監聽器接收和處理其他參與者發出的訊息。由於沒有中間協調點,整個過程靠參與自己進行相互協調。
SAGA優點
靈活性:Saga 允許每個小事務獨立管理,提高了系統的靈活性。
減少資源鎖定:由於 Saga 不需要在事務執行過程中持續佔用資源,因此可以減少長時間的資源鎖定,提高系統的併發能力。
容錯性:Saga 透過定義補償操作來處理失敗,增強了系統的容錯能力。
適用於微服務架構:在微服務架構中,Saga 可以跨服務邊界管理事務,每個服務獨立處理自己的事務和補償邏輯。
SAGA缺點
複雜性:實現 Saga 需要定義每個小事務的補償操作,這可能會增加系統的複雜性。
資料一致性:Saga 不能提供 2PC 那樣的即時一致性保證,它只能保證最終一致性,這在某些業務場景中可能是不夠的。
補償操作的難度:在某些情況下,補償操作可能很難實現,尤其是當事務有副作用時(比如傳送了一個不可撤銷的通知)。
測試和除錯:由於 Saga 涉及多個服務和補償邏輯,測試和除錯可能會更加困難。
SAGA適用場景
在選擇使用 Saga 模式時,需要考慮業務場景是否適合最終一致性,實現補償邏輯的成本是和可靠性。對於需要高度一致性保證的場景,其他事務管理機制可能更合適。
TCC模式與Saga模式有一些相似之處。兩者都是將大型事務拆分為多個小事務,並支援補償操作。然而,TCC協議更加關注於資源鎖粒度的控制,而Saga模式更加註重長時間執行事務的處理和補償機制。
6. 本地訊息表
本地訊息表事務(Local Message Table Transaction)是一種 可靠的訊息事務機制。核心思想是將分散式事務拆分成本地事務進行處理,在該方案中主要有兩種角色:事務主動方和事務被動方。
本地訊息表流程
基本思路就是:
事務主動方:"需要額外建立一張訊息表,記錄事務訊息傳送狀態。訊息表和業務資料要在同一個事務裡提交,要在一個數據庫裡面。訊息透過MQ傳送到消費方,如果訊息傳送失敗,可透過重試或定時任務傳送到MQ。
事務被動方,需要消費訊息,並完成自己的業務邏輯。此時如果本地事務處理成功,表明已經處理成功了,如果處理失敗,那麼就會重試執行。如果是業務上面的成功,可以給事務主動方傳送一個業務訊息,通知事務主動方標記或刪除事務訊息,需要可重入。
本地訊息表優點
● 實現邏輯簡單,開發成本低;
● 比較好的容錯性,弱化了對MQ元件的特性依賴;
本地訊息表缺點
● 事務訊息與業務強耦合,不可公用;
● 本地訊息表基於資料庫支援,事務訊息佔用業務IO資源,高併發場景有效能問題。
7. MQ事務訊息
MQ事務訊息是一種訊息事務機制,它利用了訊息佇列本身的機制實現分散式事務。
MQ事務訊息流程
以RocketMQ的事務訊息為例,透過事務訊息實現分散式事務的流程如下:
發起方傳送half半事務訊息會給RocketMQ,此時訊息的狀態Prepared,此時消費方還不能消費到此訊息。
發起方進行本地事務操作,執行程式碼邏輯。
發起方給RocketMQ確認提交訊息,此時消費方纔能消費到這條訊息。
MQ事務訊息優點
訊息資料獨立儲存,降低業務系統與訊息系統之間的耦合。
吞吐量優於本地訊息表方案。
MQ事務訊息缺點
一次訊息傳送需要兩次網路請求(half訊息 + commit/rollback)。
發起方需要實現訊息回查介面;
8. 最大努力通知
最大努力通知型(Best-effort delivery)是最簡單的一種 柔性事務,適用於一些最終一致性時間敏感度低的業務,且被動方處理結果不影響主動方的處理結果。
● 不可靠訊息:業務主動方,在完成業務處理之後,向業務活動的被動方傳送訊息,直到通知N次(時間退避)後不再通知,允許訊息丟失(不可靠訊息);
● 定期校對:業務被動方,根據定時策略,向業務活動主動方查詢(主動方提供查詢介面),恢復丟失的業務訊息;
最大努力通知與可靠訊息最終一致性的區別:
● 解決方案思想不同
○ 可靠訊息:發起通知方需要保證將訊息發出去,並且將訊息發到消費方,訊息的可靠性由發起方來保證。
○ 最大努力通知:發起方盡最大的努力將業務處理結果通知到接收方,但是訊息可能接收不到。此時需要消費方主動呼叫發起方的介面查詢業務處理結果,通知的可靠性關鍵在消費方。
● 業務場景不同
○ 可靠訊息:關注的是交易過程的事務一致,以非同步的方式完成交易。
○ 最大努力通知:關注的是交易後的通知事務,即將交易結果可靠的通知出去。
● 技術解決方向不同
○ 可靠訊息:要解決訊息從發出到接收的一致性,即訊息發出並且被接收到。
○ 最大努力通知:無法保證訊息從發出到接收的一致性,只提供訊息接收的可靠性機制。可靠機制是,最大努力的將訊息通知給接收方,當訊息無法被接收方接收時,由接收方主動查詢訊息(業務處理結果)。
最大努力通知適用場景
典型的使用場景:如銀行通知、商戶通知等,適用於一些訊息通知類場景。
綜合對比
分散式事務實踐方案
1. 本地共享記憶體機制
本地共享記憶體機制借鑑本地訊息表和儲存操作日誌原理,將事件資訊寫入共享記憶體,共享記憶體使用環狀佇列管理事件資訊。將事務拆分為關鍵事務和一系列子事務,事務主動方負責寫共享記憶體和執行關鍵事務,透過監測服務非同步推送子事務執行,並透過狀態機保障最終所有子事務按照預期順序執行。
流程
事務主動方收到請求後先將事件寫入共享記憶體佇列,記憶體寫入成功後執行關鍵事務;
監測服務出隊事件訊息,查詢關鍵事務儲存資料,確定是否執行成功,如果失敗直接放棄該事件訊息;
按照預期子事務順序執行子事務,如果遇到子事務執行失敗,標記執行進度重新寫入共享記憶體佇列;至到所有子事務執行完成;
優點
● 實現簡單:本地共享記憶體寫事件訊息與關鍵事務儲存隔離,子事務流程集中管理方便
● 擴充套件性強:事務擴充套件能力強
● 鬆耦合:所有子事務沒有強關聯,很符合微服務架構需要
缺點
● 單點:事件訊息單機儲存,遇到單點問題時可能會丟訊息
● 效能差:事務主動方與監測服務強關聯,資源共享會爭搶資源,效能在高併發場景有侷限;同時爲了保障順序執行,無法並行執行,子事務比較多情況吞吐量上不去
● 丟訊息:資料讀取後,遇到異常退出事件資訊未及時寫入記憶體會丟失改事件
● 時序無保障:事件訊息隨單機儲存,在多節點部署場景,無法保障時序
2. 檢測儲存binlog/oplog
不少儲存透過記錄資料庫中的寫操作到日誌,以支援資料複製、故障恢復和資料同步等功能。同時也支援操作日誌對外watch的能力,供業務使用。比如mysql 的binlog,mongodb的oplog,oracle的redo log等,透過這些儲存提供的相應watch能力,把相關資料轉存到MQ,非同步進行事務操作,下面列舉部分儲存:
● MySQL:MySQL 提供了 Binlog API,透過該 API 訂閱和消費 binlog 事件
● Oracle:OEM 提供了一組 RESTful API 介面,可以用於監控和管理 Oracle 資料庫。這些 API 可以用於查詢和運算元據庫的各種日誌資訊,包括 Redo Log、歸檔日誌等
● Mongodb:oplog,透過 Change Streams,可以訂閱 MongoDB 集合的變更事件並監聽事件
流程
使用儲存操作日誌,事務主動方只需要關注寫入儲存就能做到儲存與MQ訊息一致,事務被動方消費訊息自己保障業務,兩者完全解耦,並具備比較好的業務擴充套件能力。
監測服務保障操作事件至少一次到達MQ元件,事務被動方需要提供可重入能力;同時監測服務需要規避單機問題,需要引入合適的分散式協調策略完成保障一次事件儘量只一次同步到MQ,以下是一些常用的方式:
分散式鎖:透過分散式鎖,保障當前只有一個儲存事件只有一個監測服務寫入到MQ元件,需要解決鎖釋放、死鎖、腦裂等問題。可以使用樂觀鎖和悲觀鎖等,也可以用成熟的鎖元件,如redis、zookeeper、etcd;
共識選舉演算法:使用共識演算法,保障只有一個服務能watch對應日誌事件;如raft、paxos
優點
邏輯簡單:事務主動方只需要寫儲存,不需要關注事務訊息傳送;
可擴充套件性強:接入MQ元件,下游可以隨意擴充套件事務被動方;
最終一致性強:寫入儲存的訊息,最終一定可以落到MQ元件,藉助MQ重試和私信佇列等機制,可以保證訊息最終一致;
缺點
合適的鎖/選舉機制:監測服務需要規避單點部署,多點部署會導致儲存日誌多次消費寫入MQ,需要引入鎖/選舉機制保障一次事件訊息儘量只有一個單點被監測到寫入MQ;
訊息重複:事件訊息保障at least once,需要事務被動方可重入;
3. 訊息佇列
流程
與本地共享記憶體機制類似流程,由寫共享記憶體變更為MQ元件,核心執行流程:
事務主動方收到請求後先將事件寫入MQ,MQ寫入成功後執行關鍵事務
監測服務消費事件訊息,查詢關鍵事務儲存資料,確定是否執行成功,如果失敗直接放棄該事件訊息
按照預期子事務順序執行子事務,子事務執行失敗可以退避重試或入重試佇列
與本地共享記憶體方案對比,資料分散式儲存,規避單機丟失資料風險,同時支援業務指定串並行執行子事務,可以平行擴充套件子事務,在最終一致場景更加鬆耦合和簡單。
優點
● 簡單:MQ元件非常成熟,且具備比較高可用性和高效能;
● 鬆耦合:子事務和關鍵事務完全結偶,由業務定義子事務串並行關係;
● 緩衝消峰:MQ可以作為緩衝區,處理生產者和消費者之間的速率差異。它可以緩衝和平滑處理高峰期的訊息流量,避免系統過載和資源浪費;
● 時序保障:MQ通常可以保證訊息的順序性。子事務可以按照訊息的順序進行處理,以確保訊息的順序性和一致性;
缺點
● 儲存壓力大:所有子事務都需要查詢關鍵事務儲存狀態做對賬,增加關鍵儲存壓力;
● 複雜的除錯和故障排除:當系統中涉及訊息佇列時,除錯和故障排除可能變得更加複雜。追蹤訊息的流動、理解訊息處理的狀態和處理錯誤可能需要額外的工作和工具支援;
4. 冪等可重入
冪等表示一次和多次請求應該不具有副作用,做到重複操作最終也能一致,下面列舉重複場景:
● 前端重複提交:使用者快速重複點選多次,造成後端生成多個內容重複的訂單
● 介面超時重試:介面超時不確定被調方是否執行成功,需要重試保障投遞成功
● 訊息重複消費:MQ訊息中介軟體,訊息重複生產和消費
下面是一些實現冪等性的常見方法:
更新前檢查:
介面先檢查前置事件是否已執行成功,如果已執行成功,執行後續任務保障狀態一致。比如:關注場景,需要修改關注列表和粉絲列表,如果關注列表內沒有執行成功,可以不執行粉絲列表更新操作;也可以檢查唯一id是否已執行成功,成功就返回成功,否則執行事務;
Token機制:
Token機制核心解決上游重放,流程如下:
○ 上游請求Token服務查詢token,Token服務把token儲存起來並返回
○ 上游再攜帶對應token請求事務服務,如果token校驗不存在,則返回無效引數錯誤;如果token存在則刪除token再執行事務
● 鎖機制:悲觀鎖/樂觀鎖/分散式鎖等,將資源鎖起來,獲取鎖成功的執行事務,獲取鎖失敗不執行
● 資料庫去重表 :引入唯一id,對於重複的id,唯一索引返回失敗
● 狀態機機制:一個完整事務拆分為多個子事務,每個子事務執行完成後記錄進度,對於已完成的事務不再執行,執行未完成的子事務,直到完整事務完成
5. 專案演練
搶購場景:商場線上一批數量有限的商品(每種商品數量有限)促銷,每位使用者限購最多2件,需要記錄每宗商品售賣數量、使用者購買歷史。
分析過程如下:
需求分解
事務A:商品庫存管理
事務B:使用者購買次數管理
事務C:使用者支付
事務D:商品已售數量管理
事務E:使用者購買歷史
問題分析
設計多個事務,如果用本地事務,可以一次原子操作保障一致性,但面臨搶購高併發場景遇到可用性問題。按照最終一致方案來分析設計,先設計流程:
● 前端呼叫:
引入token機制,避免使用者反覆操作帶來的無效請求;引入全域性唯一id:訂單id,能夠更新前檢查訂單狀態;
● 後臺流程:
如果先扣庫存,遇到使用者次數沒有購買次數會有異常。邊界場景是所有庫存都被沒有購買條件的使用者鎖住了庫存,商品售賣被影響(黑產攻擊);
如果先使用者支付,遇到商品沒有庫存會影響使用者體驗,同時退款複雜度高(一般支付是第三方扣費);
從使用者體驗和安全形度,設計先扣使用者購買次數,再扣商品庫存,之後再使用者支付;使用者支付成功後再增加商品已售數量和修改使用者購買歷史;流程如圖:
具體流程如下:
請求token管理服務獲取token;
按照選擇商品請求訂單管理服務,獲取訂單id(全域性唯一),訂單管理服務儲存訂單資訊,並將訂單資訊寫入訂單MQ;(透過本地事務表或MQ事務訊息保障訂單儲存與訂單MQ一致),整體依託訂單id完成狀態機跟進訂單進度;
呼叫購買次數管理服務扣減使用者次數,需保障可冪等重入以及回滾功能;
呼叫商品庫存管理服務口徑庫存,需保障可冪等重入以及回滾功能;
呼叫支付管理服務支付商品;商品支付保障購買訊息一定寫入到購買成功MQ;
已售商品總數服務和使用者購買歷史服務訂單id可重入(比如更新前檢查,將訂單id和資料一起更新,之後可以從資料按需刪除已完成訂單id);
訂單對賬服務消費訂單MQ流水,追蹤訂單執行狀態;如果庫存不夠,可以直接返還購買次數,也可以依託非同步回滾購買次數;如果使用者取消支付/超時未支援,同樣需要回滾庫存和使用者購買次數;如果使用者支付成功,則需要保障商品總數、使用者購買歷史和訂單狀態一定完成修改;
總結
在選擇分散式事務解決方案時,需要根據業務需求、系統複雜度、效能要求等因素進行權衡。
例如,對於業務場景要求資料的一致性非常高,且可以接受一定程度的效能損失時,2PC 或者 3PC 是很好的選擇。
對於複雜業務流程中的分散式事務,需要在業務層進行更細粒度控制時,TCC 是一個好的選擇。比如,使用者在電商平臺下單購買商品,涉及到庫存、賬戶餘額、積分等多個服務的資料變更。
而對於可容忍短時間內資料不一致的業務,則可以考慮最終一致性相關的解決方案,如:本地訊息表、訊息事務及最大努力通知方案等等。
因此,當我們探討分散式事務時,不僅要把握好使用者痛點和實際需求,還要結合每個分散式事務解決方案的特點。