電商大促:庫存 1 件,用戶 A、B 同時下單。必須保證只有一單成功,不超賣、不少賣。
查詢方式 鎖類型 並發行為 SELECT ...無鎖(MVCC 快照讀) A、B 同時讀到 stock=1,皆通過判斷 → 超賣 SELECT ... LOCK IN SHARE MODE共享鎖 (S) A、B 可共存,但 UPDATE 需等 S 鎖釋放 → 仍可能超賣 SELECT ... FOR UPDATE排他鎖 (X) A 先得鎖,B 阻塞等待 → A 提交後 B 讀到 stock=0 → 正確 關鍵細節
- 必須在事務中:自動提交模式下
FOR UPDATE鎖即得即釋,無保護效果- 走索引:
WHERE id = 1命中主鍵,鎖定單行;無索引會升級為表鎖/間隙鎖,嚴重降低並發- 業務判斷在應用層:資料庫只負責鎖與扣減,邏輯判斷放應用代碼
- 超時控制:
innodb_lock_wait_timeout=50(預設 50s),超時自動回滾- 死鎖預防:多表更新時統一按主鍵遞增順序加鎖
進階:樂觀鎖替代方案(低並發/重試成本低)
方案 並發能力 實現複雜度 適用場景 SELECT FOR UPDATE (悲觀鎖) 高(毫秒級持鎖) 低 庫存扣減、座位鎖定、強一致性 樂觀鎖 (version) 極高(無鎖) 中 衝突率低、可重試、高吞吐 Redis Lua 原子扣減 極高(單執行緒) 中 極高並發、最終一致可接受 小結
事務 + SELECT ... FOR UPDATE + 應用層判斷 + UPDATE 是 MySQL 防超賣的經典模式。關鍵三點:**在事務中、走主鍵索引、鎖住再判斷**。高併發可升級 Redis Lua 或樂觀鎖,但邏輯更複雜。