冪等:N 次操作與 1 次操作結果相同。高併發系統中,網路重試、重複提交、訊息重發皆可能導致非冪等操作產生嚴重後果(如重複下單、重複扣款)。
SELECT 無副作用,天然滿足冪等。無需額外處理。
-- 版本號樂觀鎖 UPDATE Table_X SET Name = #Name#, Version = Version + 1 WHERE Version = #Version#; -- 業務條件樂觀鎖(扣庫存) UPDATE Table_X SET Qty = Qty - #SubQty# WHERE Qty - #SubQty# >= 0;
適用於更新操作,利用資料庫行級鎖與條件判斷保證冪等。
CREATE TABLE Biz_Dedup (
Biz_Type VARCHAR(20),
Biz_Key VARCHAR(100),
Created_At DATETIME DEFAULT GETDATE(),
PRIMARY KEY (Biz_Type, Biz_Key)
);
插入業務資料前,先插入去重表。利用主鍵/唯一索引衝突攔截重複。適合多業務類型、去重邏輯統一場景。
使用 Redis (Redisson)、ZooKeeper、etcd 實現分散式鎖:
RLock lock = redisson.getLock("biz:order:" + orderId);
lock.lock();
try {
// 業務邏輯
} finally {
lock.unlock();
}
適用於分散式系統、無法確定唯一業務鍵場景。注意鎖超時、續約、可用性設計。
DELETE FROM Table WHERE Id = #Id#; -- 多次執行結果相同 -- 或軟刪除 UPDATE Table SET Deleted = 1 WHERE Id = #Id# AND Deleted = 0;
刪除天然冪等(結果態相同),軟刪除需加條件避免重複更新觸發器。
CREATE UNIQUE INDEX UX_Order_User_Date ON Orders (UserId, OrderDate, ProductId);
資料庫層面強制唯一。API 層面可增加客戶端生成 UUID,伺服端以 UUID 去重:
// 客戶端生成
String idempotencyKey = UUID.randomUUID().toString();
// 伺服端驗證
if (redis.setnx("idem:" + idempotencyKey, "1", 24, TimeUnit.HOURS)) {
// 執行業務
}
訂單狀態:建立 → 付款 → 發貨 → 完成
UPDATE Orders SET Status = 'PAID' WHERE OrderId = #Id# AND Status = 'CREATED'; -- 僅允許 CREATED → PAID
非預期狀態轉換(如 PAID → CREATED)直接拒絕,保證狀態流向單向性與冪等。
| 場景 | 推薦方案 | 備註 |
|---|---|---|
| 單機更新 | MVCC 樂觀鎖 | 簡單高效 |
| 多業務類型去重 | 獨立去重表 | 統一管理 |
| 分散式系統插入 | 分散式鎖 + 唯一索引 | 雙重保險 |
| API 重複提交 | 客戶端 UUID + Redis 去重 | 應用層攔截 |
| 工作流狀態流轉 | 狀態機條件更新 | 業務語義保護 |
冪等設計是高併發系統穩定性基石。資料庫約束為底線,應用層邏輯為防線,分散式協調為補強。根據業務場景組合使用,避免單點失效。