首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 更多程式語言 >> 程式設計綜合問答

分布式 Java Web 多節點部署下的並發控制:從樂觀鎖到分布式鎖的完整方案

     編輯:程式設計綜合問答

場景還原

多實例部署(Nginx 負載均衡),共享同一張表 `task`。用戶點擊「認領」,需保證**同一條任務只能被一個用戶認領成功**,防止超賣/重複分配。

方案演進:從簡單到強健

Level 1:前端攔截(必要但不充分)

// 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 竞態**:檢查與提交間隔內,其他請求可能已搶先入庫。

Level 2:數據庫唯一約束(兜底防線)

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 壓力與日誌噪音。

Level 3:樂觀鎖(版本號/時間戳)

-- 表加 version 欄位
UPDATE task SET claimer_id=?, version=version+1 
WHERE id=? AND version=? AND status=0;

應用層重試 1-2 次,適合「衝突率低」場景。

Level 4:Redis 分布式鎖(高併發核心方案)

// 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";

鎖 Key 設計原則

  • 粒度最小:`lock:task:{id}` 而非 `lock:task`
  • 租約時間 > 業務邏輯最長執行時間 + 網絡抖動
  • 看門狗自動續期(Redisson 默認開啟)防止業務未結束鎖失效

Level 5:本地鎖 + 分布式鎖雙重保障(極高併發)

// 進程內優先攔截
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)

生產級最佳實踐清單

  1. **數據庫層面必須有唯一約束或條件更新**—— 這是最後防線,任何上層方案失效都不會產生髒數據
  2. **業務邏輯冪等設計**:同一用戶重複點擊認領接口應返回相同結果,不報錯
  3. **鎖超時時間合理設置**:太短業務未跑完釋放,太長阻塞後續請求
  4. **監控告警**:鎖獲取失敗率、DB 死鎖、慢查詢實時監控
  5. **降級預案**:Redis 掛掉時自動降級到 DB 悲觀鎖 (`SELECT ... FOR UPDATE`)

總結

多節點並發控制的核心原則:**數據庫為準、應用層加鎖提效、前端只做體驗優化**。不要試圖用單一技術解決所有問題,分層防禦(DB 約束 → 樂觀鎖 → 分布式鎖 → 本地鎖)才是工程成熟度的體現。對於「任務認領」這類典型場景,**`UPDATE ... WHERE status=0` + Redis 分布式鎖** 是性能與安全性的最佳平衡點。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved