多實例部署(Nginx 負載均衡),共享同一張表 `task`。用戶點擊「認領」,需保證**同一條任務只能被一個用戶認領成功**,防止超賣/重複分配。
// Vue/React 伪代码
async function claim(taskId) {
const { data } = await axios.get(`/api/task/check/${taskId}`);
if (data.claimed) return alert('已被認領');
await axios.post('/api/task/claim', { taskId });
}
❌ **TOCTOU 竞態**:檢查與提交間隔內,其他請求可能已搶先入庫。
ALTER TABLE task ADD CONSTRAINT uk_claimer UNIQUE (claimer_id);
-- 或 狀態機:status=0 可認領,status=1 已認領
UPDATE task SET claimer_id=?, status=1, claim_time=NOW()
WHERE id=? AND status=0; -- 關鍵:WHERE 條件包含舊狀態
✅ **原子性**:單條 SQL 更新+條件檢查在數據庫層面原子執行。
✅ **零代碼侵入**:無需引入額外中間件。
⚠️ **高併發下大量「更新 0 行」報錯**,增加 DB 壓力與日誌噪音。
-- 表加 version 欄位
UPDATE task SET claimer_id=?, version=version+1
WHERE id=? AND version=? AND status=0;
應用層重試 1-2 次,適合「衝突率低」場景。
// Redisson / Lua 腳本實現
String lockKey = "lock:task:" + taskId;
RLock lock = redisson.getLock(lockKey);
boolean acquired = lock.tryLock(3, 5, TimeUnit.SECONDS); // 等3s,租約5s
if (acquired) {
try {
// 再次確認 DB 狀態(雙重檢查)
if (taskMapper.canClaim(taskId)) {
taskMapper.claim(taskId, userId);
return "success";
}
return "already_claimed";
} finally {
lock.unlock();
}
}
return "lock_timeout";
// 進程內優先攔截
synchronized (taskId.intern()) { // 或 ConcurrentHashMap
// 獲取分布式鎖...
}
減少對 Redis 的無效請求,但**不能替代分布式鎖**(多實例間無感知)。
併發量 < 100 QPS → Level 2 (DB 唯一約束/條件更新) 即可
併發量 100-1000 → Level 3 (樂觀鎖) + Level 2 兜底
併發量 > 1000 → Level 4 (Redis 分布式鎖) + Level 2 兜底
秒殺/搶購場景 → Level 4 + Lua 腳本原子檢查+扣減 + 異步削峰 (MQ)
多節點並發控制的核心原則:**數據庫為準、應用層加鎖提效、前端只做體驗優化**。不要試圖用單一技術解決所有問題,分層防禦(DB 約束 → 樂觀鎖 → 分布式鎖 → 本地鎖)才是工程成熟度的體現。對於「任務認領」這類典型場景,**`UPDATE ... WHERE status=0` + Redis 分布式鎖** 是性能與安全性的最佳平衡點。