高併發秒殺場景下,庫存扣減必須保證「查詢-判斷-更新」原子性。SELECT ... FOR UPDATE 在事務中鎖定目標行,阻塞其他事務讀寫,是防超賣的核心手法。
START TRANSACTION;
-- 1. 鎖定庫存行 (排他鎖,阻塞其他 FOR UPDATE / UPDATE / DELETE)
SELECT stock FROM goods WHERE id = 1 FOR UPDATE;
-- 2. 應用層判斷
IF stock > 0 THEN
-- 3. 扣減
UPDATE goods SET stock = stock - 1 WHERE id = 1;
COMMIT;
ELSE
ROLLBACK;
-- 返回「售罄」
END IF;
| 操作 | 鎖類型 | 相容性 |
|---|---|---|
SELECT ... FOR UPDATE | X 鎖 (排他) | 阻塞其他 X 鎖、S 鎖 (LOCK IN SHARE MODE) |
SELECT ... LOCK IN SHARE MODE | S 鎖 (共享) | 相容 S 鎖,阻塞 X 鎖 |
普通 SELECT | 無鎖 (MVCC 快照讀) | 不阻塞,讀到舊版本 |
FOR UPDATE 鎖即釋放,失去保護作用。WHERE 條件必須走索引 (主鍵/唯一/普通),否則升級為表鎖,並發度歸零。ERROR 1213 (Deadlock) 重試。innodb_lock_wait_timeout 預設 50s,高併發建議降低至 5-10s 快速失敗。UPDATE goods SET stock = stock - 1, version = version + 1
WHERE id = 1 AND stock > 0 AND version = @old_version;
-- affected_rows = 1 成功,0 失敗重試
樂觀鎖無鎖開銷,適合衝突率低場景;悲觀鎖 (FOR UPDATE) 適合衝突率高、強一致性場景 (如扣款、庫存)。