萬盛學電腦網

您现在的位置: 萬盛學電腦網 >> 程式設計 >  >> 資料庫 >> MySQL 教學

MySQL 事務 + SELECT ... FOR UPDATE 實現庫存扣減防超賣

業務場景

高併發秒殺/搶購:庫存量少(如 1),大量用戶同時下單,必須保證不超賣。

核心機制

SELECT ... FOR UPDATE 在事務中對讀取行加排他鎖(X 鎖),阻塞其他事務的 FOR UPDATE 或 LOCK IN SHARE MODE 讀取,以及該行的 UPDATE/DELETE。讀取未加鎖的 SELECT 不受影響(MVCC 快照讀)。

標準流程

BEGIN;
SELECT stock FROM goods WHERE id = 1 FOR UPDATE;  -- 加鎖讀取
-- 應用層判斷 stock > 0
UPDATE goods SET stock = stock - 1 WHERE id = 1;
COMMIT;

併發測試

用戶 A 執行上述事務,用戶 B 同樣執行 SELECT ... FOR UPDATE 會阻塞等待,直到 A 提交或回滾。鎖釋放後 B 讀到最新 stock(已扣減),判斷為 0 放棄下單,達到防超賣。

關鍵注意點

  • 必須在事務中使用,自動提交模式下 FOR UPDATE 鎖即釋放,失去保護。
  • 索引命中:WHERE 條件必須走索引(最好是主鍵),否則會鎖全表(間隙鎖/表鎖),嚴重降低併發。
  • 死鎖風險:多表更新時保持相同加鎖順序;設定 innodb_lock_wait_timeout 避免無限等待。
  • 長事務禁忌:事務內避免 RPC/HTTP 呼叫、複雜運算,持鎖時間過長會阻塞大量線程。

進階:樂觀鎖替代方案

UPDATE goods SET stock = stock - 1, version = version + 1
WHERE id = 1 AND stock > 0 AND version = @old_version;

應用層重試機制配合樂觀鎖,適用於衝突率較低場景,無鎖等待開銷。

copyright © 萬盛學電腦網 all rights reserved