項目經驗處理

訂單取消和支付成功并發問題

這是一個非常經典且重要的分布式系統問題。訂單取消和支付成功同時發生,本質上是一個資源競爭問題,核心在于如何保證兩個并發操作對訂單狀態的修改滿足業務的最終一致性(即一個訂單最終只能有一種確定的狀態)。


核心業務原則與狀態機

首先,必須明確一個堅不可摧的業務規則:一個訂單一旦支付成功,就絕不能被取消。 反之,一個訂單如果先被成功取消,那么后續的支付也應該失敗。

這通常通過訂單狀態機來實現:

  • 初始狀態:待支付
  • 可變為:已支付 (支付操作) 或 已取消 (取消操作)
  • 已支付已取消 是兩個終點狀態,不能再相互轉換。

我們的所有技術方案都是為了保護這個狀態機的正確流轉。


方案一:數據庫悲觀鎖 (Pessimistic Locking) - 簡單粗暴

適用場景: 單體架構,數據庫壓力不大,并發沖突相對較少的場景。

實現原理: 在修改訂單狀態前,先通過 SELECT ... FOR UPDATE 鎖定數據庫中的這條訂單記錄。這樣其他事務在嘗試修改這條記錄時會被阻塞,直到鎖被釋放,從而保證了操作的串行化。

實現步驟(以支付和取消同時發生為例):

  1. 無論是支付回調邏輯還是取消邏輯,在開始處理時,首先開啟一個數據庫事務。
  2. 在事務中,使用 SELECT * FROM orders WHERE order_id = ? FOR UPDATE 查詢并鎖定訂單記錄。
  3. 檢查訂單的當前狀態:
    • 如果狀態是 待支付,則繼續執行支付或取消操作,更新狀態。
    • 如果狀態已經是 已支付,則取消操作應識別到此狀態,直接返回“取消失敗,訂單已支付”并結束邏輯。
    • 如果狀態已經是 已取消,則支付操作應識別到此狀態,直接返回“支付失敗,訂單已關閉”并結束邏輯。
  4. 提交事務,釋放鎖。

優點:

  • 實現簡單,直接利用數據庫的能力。
  • 強一致性保證。

缺點:

  • 性能瓶頸:并發高時,大量請求會被阻塞,數據庫連接池容易被耗盡的,響應時間變長。
  • 不適合分布式系統:如果應用是集群部署,多個節點間的數據庫連接是共享的,這個方法仍然有效,但數據庫本身會成為單點瓶頸。
  • 死鎖風險:需要仔細控制業務的加鎖順序。

方案二:數據庫樂觀鎖 (Optimistic Locking) - 推薦常用

適用場景: 絕大多數并發場景,特別是讀多寫少的情況。單體和服務化架構都適用。

實現原理: 不對數據加鎖,而是通過一個版本號(version)字段或時間戳來實現。在更新時,檢查版本號是否和最初讀取時一致,如果一致則更新成功并版本號+1,否則更新失敗,意味著數據已被其他操作修改過。

實現步驟:

  1. 訂單表增加一個 version 字段(或使用 update_time 時間戳)。
  2. 支付或取消邏輯開始時,先查詢出訂單的當前狀態和當前 version(例如 version=1)。
  3. 根據業務邏輯計算下一個狀態。
  4. 執行更新操作:
    UPDATE orders 
    SET status = '已支付', version = version + 1 
    WHERE order_id = ? AND version = 1; -- 這里version是之前查出來的值
    
    UPDATE orders 
    SET status = '已取消', version = version + 1 
    WHERE order_id = ? AND version = 1;
    
  5. 檢查更新語句的影響行數(affected rows):
    • 如果影響行數為 1,說明更新成功,搶到了資源。
    • 如果影響行數為 0,說明 WHERE 條件不成立(即 version 已經變了),更新失敗。此時可以重新查詢訂單的最新狀態,并告知用戶“操作失敗,請重試”或根據最新狀態進行后續處理(例如支付時發現訂單已取消,則進行退款)。

優點:

  • 性能比悲觀鎖好很多,避免了數據庫鎖的開銷。
  • 適用于分布式環境。

缺點:

  • 需要處理更新失敗的情況,業務邏輯稍復雜(通常需要重試或提示用戶)。
  • 如果沖突頻率非常高,頻繁的重試反而會降低性能。

方案三:狀態機 + 數據庫唯一約束 - 優雅冪等

適用場景: 作為輔助手段,與樂觀鎖結合使用,提供更強的冪等性和一致性保證。

實現原理: 利用數據庫的唯一鍵約束,來防止訂單狀態被重復更新。例如,可以創建一張“訂單狀態變更流水表”。

  1. 創建訂單狀態流水表 order_status_log:
    CREATE TABLE order_status_log (id BIGINT PRIMARY KEY AUTO_INCREMENT,order_id BIGINT NOT NULL,from_status VARCHAR(20),to_status VARCHAR(20) NOT NULL,create_time DATETIME,-- 唯一約束:一個訂單的每次狀態變更必須是唯一的UNIQUE KEY uk_order_id_status (order_id, from_status, to_status)
    );
    
  2. 在更新訂單主表狀態時,在同一數據庫事務中,向流水表插入一條記錄。
    • 例如,從 待支付 變更為 已支付,則插入 (order_id, ‘待支付’, ‘已支付’, now())
  3. 如果兩個操作同時發生,比如支付和取消都通過了樂觀鎖的版本檢查,嘗試更新主表和插入流水表。由于流水表的 UNIQUE KEY uk_order_id_status (order_id, from_status, to_status) 約束,第二個插入操作必然會失敗,從而導致整個事務回滾。
  4. 最終只有一個操作能成功。

優點:

  • 提供了極強的數據一致性保證,幾乎不可能出現狀態錯亂。
  • 流水表本身也具有審計和追溯的價值。

缺點:

  • 業務邏輯更復雜,需要維護兩張表。
  • 依然依賴數據庫事務。

方案四:消息隊列 + 最終一致性 - 分布式解耦

適用場景: 大型分布式系統,業務解耦要求高,吞吐量大的場景。

實現原理: 將同步的、可能沖突的操作,通過消息隊列串行化處理。支付成功和取消請求都先發送到消息隊列,由一個消費者按順序逐個處理,從而從根本上避免并發沖突。

實現步驟:

  1. 消息生產:
    • 支付回調異步通知和用戶取消請求,都不直接處理業務邏輯。
    • 它們只負責校驗基礎參數,然后向一個特定主題的消息隊列(如 RocketMQ/Kafka)發送一條消息。消息體包含訂單ID和操作類型(e.g., {"orderId": 123, "event": "PAYMENT_SUCCESS"})。
  2. 消息消費:
    • 創建一個順序消息消費者,監聽這個主題。確保同一個訂單ID的消息被發送到同一個MessageQueue,并被同一個消費者順序處理
    • 消費者接收到消息后:
      a. 開啟事務。
      b. 查詢訂單(無需FOR UPDATE,因為消息是順序的)。
      c. 檢查訂單狀態機:
      - 若當前狀態允許執行目標操作(如狀態是待支付,目標是已支付),則更新狀態。
      - 若不允許(如狀態已是已取消,目標是已支付),則丟棄消息或記錄日志(說明發生了沖突,但由消費者優雅處理了)。
      d. 提交事務。
      e. 消費成功,確認消息。

優點:

  • 高吞吐量,性能好。
  • 徹底解耦,支付和取消的發起方不需要等待業務處理完成。
  • 通過順序消息天然解決了并發問題。

缺點:

  • 架構復雜,引入了消息隊列組件。
  • 一致性是最終一致性,處理會有毫秒級或秒級的延遲。
  • 需要保證消息的可靠投遞和不重復消費(冪等),通常消息隊列本身能提供Exactly-OnceAt-Least-Once語義,消費端需要做冪等(方案二的樂觀鎖或方案三的狀態流水正好可以用來做冪等)。

方案五:分布式鎖 - 通用方案

適用場景: 分布式系統,需要對某個分布式資源進行互斥訪問。

實現原理: 在執行業務邏輯前,先嘗試獲取一個基于訂單ID的分布式鎖(如 Redis 的 SET order_id:123 random_value NX EX 30)。只有拿到鎖的操作才能繼續執行查詢和更新訂單狀態的邏輯。

實現步驟:

  1. 支付或取消邏輯開始時,先嘗試獲取指定 order_id 的分布式鎖。
  2. 獲取成功,則繼續執行數據庫查詢和更新邏輯(此時可以用簡單的先查后改,因為鎖保證了互斥)。
  3. 獲取失敗,則重試或直接返回“系統繁忙,請稍后再試”。
  4. 業務邏輯處理完成后,釋放分布式鎖。

優點:

  • 通用性強,不依賴于數據庫特性。
  • 適用于任何需要互斥訪問的分布式場景。

缺點:

  • 引入新的組件(如Redis),增加了系統復雜性。
  • 如果鎖失效時間設置不當,可能導致鎖提前釋放(業務沒做完)或死鎖(業務做完鎖沒釋放)。
  • 性能開銷比數據庫樂觀鎖大。

總結與選型建議

方案復雜度一致性性能適用架構核心思想
悲觀鎖強一致性單體先加鎖,再操作
樂觀鎖最終一致性單體/分布式無鎖檢測,失敗重試
狀態機+約束強一致性單體/分布式利用數據庫約束防沖突
消息隊列最終一致性極好分布式異步化與串行化
分布式鎖最終一致性分布式外部組件實現互斥

給你的建議:

  1. 新手或初創項目:優先使用 方案二(樂觀鎖)。它在復雜性、性能和一致性上取得了很好的平衡,是處理這類問題最常用的手段。
  2. 中等規模項目:采用 方案二 + 方案三 組合。用樂觀鎖做更新,用狀態流水表做冪等、審計和雙重保險,非常穩健。
  3. 大型分布式系統:采用 方案四(消息隊列)。將并發請求轉為順序處理,是解決高并發問題的終極方案之一,同時也能很好地解耦系統。
  4. 方案一(悲觀鎖) 盡量少用,除非你非常確定并發量不高。
  5. 方案五(分布式鎖) 更適用于更廣泛的分布式資源競爭場景,單純為了訂單狀態這個問題,通常優先選擇樂觀鎖或消息隊列。

最終,選擇哪種方案取決于你的業務規模、技術架構和團隊對復雜性的承受能力。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/920544.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/920544.shtml
英文地址,請注明出處:http://en.pswp.cn/news/920544.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

rabbitmq學習筆記 ----- 多級消息延遲始終為 20s 問題排查

問題現象 在實現多級延遲消息功能時&#xff0c;發現每次消息延遲間隔始終為20s&#xff0c;無法按照預期依次使用20s→10s→5s的延遲時間。日志顯示每次處理時移除的延遲時間都是20000L。 問題代碼片段 1.生產者 Testvoid sendDelayMessage2() {List<Long> expireTimeLi…

軟件測試(三):測試流程及測試用例

1.測試流程1.需求分析進行測試之前先閱讀需求文檔&#xff0c;分析指出不合理或不明確的地方2.計劃編寫與測試用例測試用例用例即&#xff1a;用戶使用的案例測試用例&#xff1a;執行測試的文檔作用&#xff1a;用例格式&#xff1a;----------------------------------------…

Python:列表的進階技巧

列表&#xff08;list&#xff09;作為 Python 最常用的數據結構之一&#xff0c;不僅能存儲有序數據&#xff0c;還能在推導式、函數參數傳遞、數據處理等場景中發揮強大作用。下面介紹一些進階技巧與常見應用。一、去重與排序1、快速去重&#xff08;不保序&#xff09;nums …

【完整源碼+數據集+部署教程】硬幣分類與識別系統源碼和數據集:改進yolo11-SWC

背景意義 隨著經濟的發展和數字支付的普及&#xff0c;傳統硬幣的使用逐漸減少&#xff0c;但在某些地區和特定場合&#xff0c;硬幣仍然是重要的支付手段。因此&#xff0c;硬幣的分類與識別在自動化支付、智能零售和物聯網等領域具有重要的應用價值。尤其是在銀行、商超和自助…

萊特萊德:以“第四代極限分離技術”,賦能生物發酵產業升級

萊特萊德&#xff1a;以“第四代極限分離技術”&#xff0c;賦能生物發酵產業升級Empowering Upgrades in the Bio-Fermentation Industry with "Fourth-Generation Extreme Separation Technology生物發酵行業正經歷從 “規模擴張” 向 “質效提升” 的關鍵轉型&#xff…

外賣大戰之后,再看美團的護城河

美團&#xff08;03690.HK&#xff09;于近日發布了2025年Q2財報&#xff0c;市場無疑將更多目光投向了其備受關注的外賣業務上。毫無懸念&#xff0c;受外賣競爭和加大投入的成本影響&#xff0c;美團在外賣業務上的財務數據受到明顯壓力&#xff0c;利潤大幅下跌&#xff0c;…

R包fastWGCNA - 快速執行WGCNA分析和下游分析可視化

最新版本: 1.0.0可以對著視頻教程學習和使用&#xff1a;然而還沒錄呢, 關注B站等我更新R包介紹 開發背景 WGCNA是轉錄組或芯片表達譜數據常用得分析, 可用來鑒定跟分組或表型相關得模塊基因和核心基因但其步驟非常之多, 每次運行起來很是費勁, 但需要修改的參數并不多所以完全…

GitHub 熱榜項目 - 日榜(2025-08-29)

GitHub 熱榜項目 - 日榜(2025-08-29) 生成于&#xff1a;2025-08-29 統計摘要 共發現熱門項目&#xff1a;11 個 榜單類型&#xff1a;日榜 本期熱點趨勢總結 本期GitHub熱榜展現出三大技術趨勢&#xff1a;1&#xff09;AI應用持續深化&#xff0c;ChatGPT等大模型系統提示…

【深度學習實戰(58)】bash方式啟動模型訓練

export \PATHPYTHONPATH/workspace/mmlab/mmdetection/:/workspace/mmlab/mmsegmentation/:/workspace/mmlab/mmdeploy/:${env:PYTHONPATH} \CUDA_VISIBLE_DEVICES0 \DATA_ROOT_1/mnt/data/…/ \DATA_ROOT_2/mnt/data/…/ \DATA_ROOT_MASK/…/ \PATH_COMMON_PACKAGES_SO…sonoh…

【物聯網】關于 GATT (Generic Attribute Profile)基本概念與三種操作(Read / Write / Notify)的理解

“BLE 讀寫”在這里具體指什么&#xff1f; 在你的系統里&#xff0c;樹莓派是 BLE Central&#xff0c;Arduino 是 BLE Peripheral。 Central 和 Peripheral 通過 **GATT 特征&#xff08;Characteristic&#xff09;**交互&#xff1a;讀&#xff08;Read&#xff09;&#x…

JavaSE丨集合框架入門(二):從 0 掌握 Set 集合

這節我們接著學習 Set 集合。一、Set 集合1.1 Set 概述java.util.Set 接口繼承了 Collection 接口&#xff0c;是常用的一種集合類型。 相對于之前學習的List集合&#xff0c;Set集合特點如下&#xff1a;除了具有 Collection 集合的特點&#xff0c;還具有自己的一些特點&…

金屬結構疲勞壽命預測與健康監測技術—— 融合能量法、紅外熱像技術與深度學習的前沿實踐

理論基礎與核心方法 疲勞經典理論及其瓶頸 1.1.疲勞失效的微觀與宏觀機理&#xff1a; 裂紋萌生、擴展與斷裂的物理過程。 1.2.傳統方法的回顧與評析。 1.3.引出核心問題&#xff1a;是否存在一個更具物理意義、能統一描述疲勞全過程&#xff08;萌生與擴展&#xff09;且試驗量…

【貪心算法】day4

&#x1f4dd;前言說明&#xff1a; 本專欄主要記錄本人的貪心算法學習以及LeetCode刷題記錄&#xff0c;按專題劃分每題主要記錄&#xff1a;&#xff08;1&#xff09;本人解法 本人屎山代碼&#xff1b;&#xff08;2&#xff09;優質解法 優質代碼&#xff1b;&#xff…

AI 與腦機接口的交叉融合:當機器 “讀懂” 大腦信號,醫療將迎來哪些變革?

一、引言&#xff08;一&#xff09;AI 與腦機接口技術的發展現狀AI 的崛起與廣泛應用&#xff1a;近年來&#xff0c;人工智能&#xff08;AI&#xff09;技術迅猛發展&#xff0c;已廣泛滲透至各個領域。從圖像識別、自然語言處理到智能決策系統&#xff0c;AI 展現出強大的數…

uniapp vue3 canvas實現手寫簽名

userSign.vue <template><view class"signature"><view class"btn-box" v-if"orientation abeam"><button click"clearClick">重簽</button><button click"finish">完成簽名</butt…

頁面跳轉html

實現流程結構搭建&#xff08;HTML&#xff09;創建側邊欄容器&#xff0c;通過列表或 div 元素定義導航項&#xff0c;每個項包含圖標&#xff08;可使用字體圖標庫如 Font Awesome&#xff09;和文字&#xff0c;為后續點擊交互預留事件觸發點。樣式設計&#xff08;CSS&…

Spring Boot自動裝配機制的原理

文章目錄一、自動裝配的核心觸發點&#xff1a;SpringBootApplication二、EnableAutoConfiguration的作用&#xff1a;導入自動配置類三、自動配置類的加載&#xff1a;SpringFactoriesLoader四、自動配置類的條件篩選&#xff1a;Conditional注解五、自動配置的完整流程六、自…

(未完結)階段小總結(一)——大數據與Java

jdk8-21特性核心特征&#xff1a;&#xff08;8&#xff09;lambda&#xff0c;stream api&#xff0c;optional&#xff0c;方法引用&#xff0c;函數接口&#xff0c;默認方法&#xff0c;新時間Api&#xff0c;函數式接口&#xff0c;并行流&#xff0c;ComletableFuture。&…

嵌入式Linux驅動開發:設備樹與平臺設備驅動

嵌入式Linux驅動開發&#xff1a;設備樹與平臺設備驅動 引言 本筆記旨在詳細記錄嵌入式Linux驅動開發中設備樹&#xff08;Device Tree&#xff09;和平臺設備驅動&#xff08;Platform Driver&#xff09;的核心概念與實現。通過分析提供的代碼與設備樹文件&#xff0c;我們…

【完整源碼+數據集+部署教程】骨折檢測系統源碼和數據集:改進yolo11-EfficientHead

背景意義 骨折作為一種常見的骨骼損傷&#xff0c;其診斷和治療對患者的康復至關重要。傳統的骨折檢測方法主要依賴于醫生的經驗和影像學檢查&#xff0c;如X光、CT等&#xff0c;這不僅耗時&#xff0c;而且容易受到主觀因素的影響。隨著計算機視覺和深度學習技術的迅猛發展&a…