概述
- 在當今的互聯網時代,分布式系統已成為支撐大規模服務、高并發和高性能應用的基石
- 它們通過網絡連接多臺計算機,協同工作,共同完成任務,但這也引入了諸如數據一致性、網絡延遲、容錯性等挑戰
- 解決這些問題的關鍵在于設計和實施有效的分布式協議
- 分布式很重要,因為微服務中需要用到分布式
常見的一些問題
1 ) 場景 1
- 訂單微服務在調用庫存微服務進行扣減庫存
- 調用成功后,訂單微服務進行新建訂單
- 這里可能會存在問題
- A )先扣庫存,后生成訂單,新建訂單的時候出問題了怎么辦,這時候庫存微服務如何處理?它已經扣完庫存了
- 這種場景是庫存扣除了,但是訂單生成失敗
- B )先新建訂單,庫存不可用了,庫存扣減成功,但是扣減返回時網絡不通了,新建訂單就會超時,訂單創建失敗,并回滾了,但是庫存扣了
- 以上兩種都是問題,而且經常發生,因為都是微服務,而且存在網絡抖動
- 庫存的扣減和訂單表不一致造成的問題,實質都是分布式事務要解決的問題
2 )場景 2
- 關于支付的場景, 比如,下單了,但是不支付,也是非常正常
- 有100個用戶下單,但是不支付,庫存一直被占用
- 這是訂單超時需要解決的問題
3 )綜合來說
- 分布式微服務的好處可以解決單體的問題,但是也帶來了新的問題
- 在不斷解決問題中優化系統,讓系統更易于維護
- 使用業務上的流程規避微服務中的劣勢
Mysql事務
- 事務:通俗來說是同生共死,要么一起成功,要么一起失敗
- 官方解釋事務:事務是程序中一系列嚴密的邏輯操作,所有操作必須全部成功完成,否則所有操作中的更改會一起失敗回滾
Mysql事務的特性 ACID
- 1 )原子性 Atomicity: 最小的單位,不能再切分;原子性關心的是功能
- 2 )一致性 Consistency: 事務的執行使從一個狀態轉化為另一個狀態,但是最整個事務的完整性保持穩定,例如:A->B轉賬, A, B的總額保持一致
- 兩條不同的sql, 一個是減法,一個是加法,在高并發的時候保證sql執行的正確性,一定要加鎖來做轉賬;
- 從業務上要規避中間狀態,比如規定過一會兒再來查詢
- 一致性關心的是狀態,沒有部分成功的狀態
- 3 )隔離性 Isolation
- 多個用戶訪問數據庫的時候,比如操作同一張表,數據庫為每一個用戶開啟事務,不能被其他事務干擾,多個并發事務之間相互隔離
- 其實要達到這個效果,任意兩個并發的事務,比如T1 和 T2, 在T1的角度看來,T2要么在之前就結束,T2要么在自身結束后才能開始
- 執行的時候,不會干擾,被隔離了, 本質是一個個的執行
- 4 )持久性 Durability
- 當事務正確完成后,數據的改變是永久性的,最終是落盤的,不能一直在內存里
分布式事務
-
分布式事務不能滿足 ACID,但是在努力滿足ACID
-
盡管分布式事務設計目標是滿足ACID,但在實際應用中,由于網絡延遲、節點故障、分區、性能考量,往往需要對這些特性進行權衡
-
例如,犧牲一定程度的隔離性以換取更高的性能(BASE理論),或者采用最終一致性模型來平衡一致性與可用性(CAP定理)
-
因此,分布式事務的解決方案往往是折衷的藝術,根據具體場景選擇最合適的策略
-
下面我們從 ACID 層面來說
-
1 )原子性(Atomicity):
- 分布式事務的原子性通常是通過兩階段提交(2PC)、三階段提交(3PC)等協議來保證
- 這些協議在理論上可以實現原子性,但在實踐中存在局限性。例如,兩階段提交可能導致參與者長時間鎖定資源等待,且存在單點故障問題,若協調者故障,可能導致參與者長時間阻塞或不確定狀態
- 三階段提交雖減少阻塞,但增加了復雜度和通信成本
-
2 )一致性(Consistency):
- 分布式系統為了保證高可用性和擴展性,往往采用弱一致性模型(如最終一致性、因果一致性),犧牲了強一致性
- 這意味著在事務處理過程中,系統可能暫時處于不一致的狀態,直到數據同步完成
- 雖然通過補償事務(如Saga)可以恢復一致性,但可能引入額外延遲和復雜性
-
3 )隔離性(Isolation):
- 在分布式系統中實現嚴格的隔離性(如序列化隔離)是非常昂貴的,因為它通常需要大量的鎖或復雜的并發控制機制,這會直接影響系統的吞吐量和響應時間
- 因此,許多分布式數據庫和事務系統傾向于使用較弱的隔離級別(如讀已提交、讀未提交),這些級別雖然提高了性能但可能暴露并發事務間的數據不一致視圖問題
-
4 )持久性(Durability):
- 雖分布式事務中,持久性一般通過日志記錄和冗余備份等機制來保證,通常能夠較好滿足
- 但極端情況下,如多數據中心級別的災難、網絡分割,可能導致數據同步出現問題,影響持久性保證
- 此外,性能和持久性的平衡也是一個考慮因素,過度追求即時持久可能犧牲寫入性能
-
綜上所述,雖然分布式事務設計的目標是盡量貼近ACID原則,但在分布式環境的實際應用中
-
為了應對網絡延遲、容錯、提高可用性和擴展性,往往需要在某些方面做出妥協,如采用BASE(基本可用性、軟狀態、最終一致性、最終一致性)哲學,而不是嚴格遵循ACID
分布式系統中,哪些方面會造成數據不一致的問題
- 1 )硬件故障
- 2 )網絡抖動
- 3 )網絡擁堵
- 4 )其他原因
- 代碼bug
- 系統問題:不兼容,日志級別較低
- 斷電
- 磁盤已滿
分布式事務的常用解決方案
-
1 )二階段提交
- 兩階段提交是最傳統的分布式事務協議,分為準備階段和提交階段:
- 準備階段:協調者詢問參與者(資源管理器)是否準備好提交事務,參與者回復“準備好”或“取消”
- 提交階段:如果所有參與者都回復“準備好”,協調者命令所有參與者提交事務;若有任何參與者回復“取消”,則命令所有參與者回滾
- 優點:理論上保證了強一致性
- 缺點:性能差,阻塞問題嚴重,單點故障問題(協調者故障),不適合大規模分布式系統
- 由此衍生出了,三階段提交(3PC, Three-Phase Commit)
- 三階段提交是兩階段提交的改進版,新增了一個預提交階段以減少阻塞:
- 第一階段(CanCommit):詢問階段
- 第二階段(PreCommit):預提交,參與者準備提交
- 第三階段(DoCommit):正式提交或回滾
- 優點:減少了阻塞時間
- 缺點:復雜度增加,同樣存在單點故障問題,性能問題
- 兩階段提交是最傳統的分布式事務協議,分為準備階段和提交階段:
-
2 )TCC的補償模式
- 這是一種分布式事務解決方案,它通過將事務處理過程拆分為三個階段來確保分布式系統中的操作成功完成或在失敗時進行補償。
- 以下是TCC補償模式的階段劃分:
- A )Try階段:業務系統嘗試執行事務并鎖定所需資源。在這一階段,業務會進行業務檢查,并預留好資源,但不會真正修改資源。例如,在庫存扣減的業務中,Try階段可能會增加freeze字段的數值,將庫存凍結。
- B ) Confirm階段:如果Try階段成功,業務系統將進入Confirm階段并提交事務。在這一階段,業務系統會對之前預留的資源進行真正的修改,并確認執行業務操作。在庫存扣減的業務中,Confirm階段會真正降低庫存。
- C ) Cancel階段:如果Try階段失敗或出現其他異常情況,業務系統將回滾事務并釋放所有鎖定的資源。Cancel階段會釋放Try階段預留的資源,并進行相應的補償操作。在庫存扣減的業務中,Cancel階段會將凍結的數值返回去。
- 實踐TCC模式的注意點
- A ) 冪等性:Confirm和Cancel操作需要設計為冪等,無論調用多少次,結果都相同,以防止重試時產生副作用。
- B ) 事務補償服務:TCC模式要求為每個操作編寫額外的Confirm和Cancel邏輯,這增加了開發復雜度和維護成本。
- C )異步處理:Cancel操作和部分Confirm操作可能需要異步處理,以提高系統響應速度,但需確保異步操作的可靠投遞達。
- D ) 監控與重試:TCC模式下的操作應有完善的監控和自動重試機制,確保Confirm和Cancel能夠最終執行成功。
-
TCC模式是一種權衡了事務處理性能和一致性的解決方案,適用于那些對事務要求較高但又無法直接使用傳統ACID事務模型的分布式場景。
-
3 )基于本地消息實現的最終一致性
- 基于本地消息實現的最終一致性是分布式系統中一種常用的模式,用于確保分布式服務間的數據最終達到一致狀態,特別是在跨服務事務處理和異步通信的場景下。
- 這種模式的核心思想是通過發布-訂閱消息機制,結合消息的持久化存儲,來確保在不同服務或組件之間操作的一致性。
- 下面是基于本地消息實現最終一致性的一個基本步驟:
- 1 ) 生成消息并持久化:當一個服務(稱為生產者)需要執行一個操作,并且這個操作需要另一個服務(稱為消費者)知曉時,生產者首先在本地生成一條消息,該消息描述了已完成的操作或需要執行的請求。這條消息會被持久化存儲在本地的消息隊列中,比如數據庫的日志表。這樣即使生產者服務宕機,消息也不會丟失。
- 2 ) 發送消息:生產者隨后嘗試將消息發送給消息中間件(如RabbitMQ、Kafka等),或者直接通過HTTP、RPC等方式通知消費者服務。注意,這個步驟與消息的持久化是解耦的,即消息的發送成功與否不影響消息的持久化狀態。
- 3 ) 消費消息:消費者服務監聽消息中間件,接收到消息后進行處理。如果處理成功,則一切正常;如果處理失敗,根據設計可能會有重試機制,嘗試再次處理,也可能需要人工介入。
- 4 ) 消息確認與補償:
- 確認機制:一旦消費者成功處理了消息,通常會向生產者發送一個確認(ACK),告知操作已完成。在某些消息中間件中,這一步是自動完成的。
- 補償邏輯:如果消費者長時間未確認,或者消息處理失敗且無法恢復,生產者端可能需要啟動補償邏輯,比如重發消息、執行逆向操作或觸發人工審核等。
- 優勢
- 解耦:生產者和消費者服務高度解耦,各自獨立發展,降低了系統的耦合度。
- 可靠性:通過消息持久化,確保了即使在系統崩潰或網絡問題情況下,消息也不會丟失,提高了數據處理的可靠性。
- 伸縮性:消息隊列可以作為緩沖區,平滑處理高峰期流量,有助于系統的水平擴展。
- 應用場景
- 訂單系統:下單后,通過消息通知庫存系統減庫存、物流系統發貨等。
- 支付系統:支付成功后,通過消息通知訂單系統更新訂單狀態、積分系統增加用戶積分等。
- 微服務架構:各服務間的數據同步、事件驅動的業務流程等。
- 注意事項
- 冪等性:消費者處理消息需要設計為冪等,防止重復處理導致數據不一致。
- 消息順序性:某些場景下,消息的順序非常重要,需要選擇支持消息順序性的消息隊列或采取額外措施。
- 消息丟失與重復:要確保消息處理邏輯能妥善處理消息的丟失與重復問題,通常通過唯一ID、狀態機等機制來控制。
- 通過上述機制,基于本地消息實現的最終一致性策略能夠在分布式系統中有效地保證數據的一致性,同時保持系統的靈活性和可擴展性。
-
4 )最大努力通知
- 最大努力通知(Best Effort Notification)是一種通信協議或策略,主要用于描述在不可靠的網絡環境下,發送方盡可能地嘗試將消息送達接收方,但不保證消息一定能成功送達的機制。
- 這種策略常應用于對即時性和可靠性要求不是極其嚴格的場景,如某些金融交易的確認通知、系統異步事件通知、或是資源受限的物聯網(IoT)環境等。最大努力通知的核心特點包括:
- 盡力而為:發送方不會因為一次發送失敗就停止嘗試,而是會根據預設的策略(如重試次數、重試間隔等)進行多次嘗試發送消息。
- 不保證送達:與ACID事務中的強一致性不同,最大努力通知不保證消息一定能夠送達接收方。網絡故障、系統崩潰、消息隊列滿等情況都可能導致消息丟失。
- 可能的重復:在重試機制下,接收方可能會收到重復的消息。因此,接收方需要實現冪等性處理邏輯,即多次處理同一消息的結果與處理一次相同。
- 可配置性:最大努力通知的策略往往是可配置的,例如重試的次數、時間間隔、是否需要回執確認等,可以根據具體應用場景調整。
- 通知狀態不確定:由于沒有嚴格的確認機制,發送方往往無法確切知道消息是否以及何時被接收方成功處理。
- 應用場景
- 通知服務:如電子郵件通知、短信通知等,這些場景下消息的即時到達雖然重要,但非關鍵,允許一定程度的延遲或丟失。
- 日志收集與監控:在分布式系統中收集日志或監控信息時,數據的完整性雖好,但更重要的是系統不應因日志傳輸問題而中斷運行。
- 資源有限的設備通信:在IoT領域,傳感器或低功耗設備可能因電力、帶寬限制而采用最大努力的方式發送數據到中心服務器。
- 實現注意事項
- 冪等性設計:確保接收方對重復消息的處理不會影響業務邏輯的正確性。
- 超時與重試策略:合理設置消息發送的超時時間和重試邏輯,避免無休止的重試占用系統資源。
- 消息去重:在接收方實施機制,如利用消息ID記錄已處理消息,避免處理重復消息。
- 反饋機制:盡管不是嚴格確認,但提供某種形式的反饋機制(如發送回執、錯誤報告)可以幫助優化和監控最大努力通知的過程。
- 總之,最大努力通知策略是在犧牲一定可靠性的前提下,換取系統設計的簡化、成本的降低和對不穩定網絡環境更好的適應性。
-
5 )基于可靠消息最終一致性
- 基于可靠消息的最終一致性是一種分布式系統中常用的技術策略,用于確保不同服務或組件間的數據在一定時間內達到一致狀態,即使在面臨網絡延遲、服務故障等情況下。
- 這種方法依賴于消息隊列或消息中間件來異步傳遞消息,通過確保消息的可靠傳遞和處理,達到最終一致性。以下是基于可靠消息實現最終一致性的一些關鍵環節和原則:
- 基本流程
- 生成并發送消息:當一個服務(生產者)完成某個操作后,它會生成一個消息,該消息包含該操作的細節或所需執行的任務,并將其發送到消息隊列或消息中間件(如RabbitMQ、Kafka、Amazon SQS等)。關鍵在于確保消息的生成和發送過程是事務性的,即操作成功完成且消息成功入隊,或兩者都不發生。
- 消息持久化存儲:消息中間件通常會保證消息的持久化存儲,即使在消息尚未被消費前,系統發生故障,消息也不會丟失。
- 消費與確認:另一個服務(消費者)監聽消息隊列,拉取并處理消息。處理成功后,消費者會向消息中間件發送確認(ACK),表明消息已被正確處理。如果消息處理失敗,消息可能被重新放入隊列等待重試,或者根據策略被丟棄、記錄錯誤或發送到死信隊列。
- 冪等性設計:消費者需要設計為冪等操作,即多次處理同一個消息產生的結果與處理一次相同,避免因消息重試導致的數據不一致。
- 補償機制:在某些情況下,如果消費者無法處理消息(如依賴服務不可用),可能需要觸發補償邏輯,以撤銷或修復由已處理部分消息引起的數據不一致。
- 關鍵特性
- 可靠性:通過消息的持久化存儲和事務性發送,確保消息不丟失,提高系統的可靠性。
- 異步處理:消息隊列允許生產和消費異步進行,提高了系統的響應速度和解耦。
- 靈活性與可擴展性:容易擴展消息處理能力,通過增加消費者實例來提高處理速度,適應系統負載變化。
- 故障隔離:生產者和消費者之間的松耦合,減少了單點故障的影響。
- 應用場景
- 訂單系統:訂單創建后,通過消息通知庫存系統減少庫存、物流系統安排配送等。
- 支付系統:支付成功后,異步通知訂單系統更新訂單狀態,發送確認郵件或短信。
- 微服務架構:不同服務間的事件驅動流程,如用戶注冊后觸發郵件通知、歡迎流程等。
- 注意事項
- 消息丟失與重復處理:確保消息不丟失的同時,也要處理消息重復消費的問題。
- 消息順序性:某些場景下消息的順序很重要,需要選擇支持有序消息的中間件或額外設計邏輯。
- 監控與報警:建立有效的監控和報警機制,及時發現消息積壓、處理延遲等問題。
- 基于可靠消息的最終一致性策略是分布式系統設計中的重要組成部分,它平衡了數據一致性和系統性能、可用性之間的關系,尤其適用于高并發、分布式環境。