冪等性 Idempotence

冪等性(Idempotence) 是分散式系統和微服務架構中的一個關鍵概念, 尤其是在處理 retry 與 防止重複操作時非常重要. 簡單來說, 冪等性意味著無論你發送相同的命令多少次, 結果都是一樣的, 系統不會改變其狀態, 甚至可能不會執行重複的操作. 對於網路異常或請求超時等情況下非常重要, 因為 client 可能會 retry, client 屬於我們不可控的對象, 但我們服務方會希望避免同一操作被重複執行.
冪等性的關鍵概念
- 冪等操作 該操作是冪等的, 表示重複執行不會產生額外的影響或狀態的改變. 例如, 使用相同的數據來更新資料的狀態, 或調用建立紀錄的端點, 這些都不會導致重複紀錄的產生.
- 冪等欄位(Idempotency Key) 在許多系統的設計中, 請求或命令中都會包含一個唯一識別符(冪等欄位), 以確保如果請求被retry, 系統能夠識別這是重複操作, 可以選擇跳過執行. 冪等欄位通常是由client端產生.
Retry Attack(重試攻擊) 是一種與網絡安全相關的攻擊方式,攻擊者利用系統允許的重試機制,通過多次嘗試重複提交請求,來實現以下目標:
- 繞過安全檢查:例如,發送多次支付請求,試圖重複扣款或重複使用一次性 token。
- 猜測敏感數據:例如,不斷重試不同的憑證或密碼,試圖獲取有效的憑據。
- 利用系統異常:利用系統在某些情況下的異常行為(如高併發導致系統容量耗盡或錯誤處理),達成攻擊目的。
冪等欄位的設計原則
- 唯一性:每個請求都應該有一個唯一的標識符,避免重複。
- 可追蹤性:冪等欄位應該能讓服務端快速定位相關請求, (很類似 OpenTelemetry 中的 Trace Id)。
- 安全性:避免使用敏感內容(如用戶名、密碼)作為冪等欄位。
- 可控性:通常由client端生成,因為服務端無法完全控制請求的重發行為
肯定有人會提出, 那讓client端請求前先跟server端獲取一個冪等值, 給client發出請求時攜帶著. retry時就重複使用該冪等值. 但client端的邏輯是我們不可控制的, 搞不好他每次retry都重新獲取一個冪等值 QQ
冪等欄位的組成
冪等欄位通常是一個唯一識別符(UUID 或類似的值),但可以根據業務需求進行擴展設計。以下是常見的組成方式:
- 使用 UUID(Universally Unique Identifier)或 GUID(Globally Unique Identifier)。
idempotency_key: "4f9e8c3a-9a6b-4d7a-8c3e-123456789abc"
優點:生成簡單且不依賴具體業務邏輯。 缺點:無法反映請求的業務語義,僅適合通用場景。
- 基於業務的唯一標識符
將業務相關的資訊組合起來,生成冪等欄位。例如:
idempotency_key: "order_12345_user_67890"
優點:冪等欄位與業務強相關,便於排查問題。 缺點:欄位設計需要根據具體業務場景定制, 不容易有統一標準.
- 基於 Hash 的唯一標識符
將請求的核心參數內容(如用戶 ID、訂單 ID、請求時間等)拼接後產生 hash value 作為冪等欄位。
idempotency_key: SHA256("user_67890_order_12345_20250124T095200")
優點:減少冪等欄位的長度,並隱藏業務細節。 缺點:需要額外的hash計算,增加了一些處理成本。
- 基於 Timestamp 的唯一標識符
在唯一標識符中加入時間戳,確保每次請求的冪等欄位不同。Snowflake Id 和 UUIDv7 就基於這種概念.
idempotency_key: "4f9e8c3a-9a6b-4d7a-8c3e-123456789abc_20250124T095200"
優點:適合需要頻繁生成冪等欄位的場景。 缺點:client 端如果是使用發出請求時的 timestamp 來產生唯一標識符, 會影響重試機制的有效性。
數字或唯一標識符字串(UUID這類), 各有優缺點. 數字的優點是好比較, payload size小; 缺點是極易被猜測出來範圍. 唯一值字串反過來, 但缺點大部分能用鈔能力解決.
應用場景
假設是在電商或金流系統中進行支付. 你發送了一次支付請求, 但因為網路問題, client 不確定支付是否成功.
- 如果沒有冪等性的設計, client 端 retry 請求可能導致支付被重複處理.
- 如果有冪等性的設計, client 端在retry之後, 服務方檢查到此請求已處理過, 通常會回傳原始的回應內容, 而選擇不會重複執行操作.
實現冪等性的步驟
- Client 端產生冪等欄位與值
- Server 端儲存冪等欄位與值
- Server 端檢查冪等欄位與值是否存在
- 如果已經存在, 回傳儲存的結果而不重複執行操作
- 不存在, 則執行操作並儲存冪等欄位與值, 應設置冪等欄位的 Expiration, 避免儲存列表無限增長
冪等欄位, Nullifier, Nonce
當我們談到冪等欄位會發現還有兩個術語的改念與這很向, 分別是Nullifier 和 Nonce 時,這三個名詞都與分散式系統、區塊鏈和密碼學中的「唯一性」和「防止重複操作」的需求密切相關。雖然它們的具體用途和應用領域不同,但核心概念有一定的相似性。以下是對這三個名詞的統一說明及比較。
Nullifier 和 Nonce 可以被視為冪等欄位的一種實現形式,只是它們的應用領域和具體功能略有不同。 統一的核心概念 這三個名詞的共同點在於:
唯一性:每個值都必須是唯一的,避免重複操作或數據衝突。 一次性使用:一旦被使用,就不能再次重複使用,從而保證系統的正確性和安全性。 防止重複執行或攻擊:這三者的設計目的是防止因重複執行請求、交易或操作而導致的錯誤或攻擊。
三者的具體定義與應用場景
冪等欄位(Idempotency Key)
定義:冪等欄位是一個唯一的標識符,用於標記一次操作(如 API 請求)是否已經執行過,從而保證即使重複執行相同的操作,系統的狀態也不會改變。
應用場景: 分散式系統:當客戶端因網路超時或錯誤而重發請求時,服務端可以通過檢查冪等欄位來確保操作不會被重複執行。 支付系統:避免同一支付請求重複扣款。
特點: 通常由客戶端生成,並附加到請求中。 服務端需要記錄已使用的冪等欄位,並檢查其唯一性。
示例:
{
"idempotency_key": "123e4567-e89b-12d3-a456-426614174000",
"action": "create_order",
"data": { ... }
}
Nullifier
定義:Nullifier 是一個唯一的標識符,用於標記某個資源或交易是否已經被使用過,特別是在隱私保護協議中,用於防止雙花(Double-Spending)或重複使用。
雙花(Double-Spending) 是區塊鏈和加密貨幣領域中的一個關鍵問題,指的是同一筆加密貨幣被用戶多次花費的情況。這種情況如果發生,會破壞加密貨幣系統的完整性和信任基礎。
應用場景: 隱私幣(如 Zcash):用於標記某筆資金是否已經被花費。 匿名交易協議(如 Tornado Cash):確保用戶提現後,不能再次使用相同的憑證進行提現。
特點: 通常由服務端或協議生成,基於某些唯一的資源(如交易 ID 或用戶密鑰)計算得出。 與隱私保護密切相關,Nullifier 本身不暴露具體資源內容。
示例:
Nullifier = Hash(User_Private_Key || Resource_ID)
Nonce
定義:Nonce 是一個用一次的數字或唯一值,用於加密協議、身份驗證或區塊鏈中,確保操作的唯一性或防止重放攻擊。JWT RFC 7800 中也有使用到Nonce。
應用場景: 區塊鏈挖礦:礦工調整 Nonce 值,直到找到滿足特定哈希條件的區塊。 交易順序:以太坊中的交易 Nonce 用於標記交易的順序,防止交易亂序或重複執行。 防重放攻擊:在 API 調用或身份驗證中,Nonce 確保每次請求都是唯一的。
特點: 可以是隨機生成的數字,也可以是遞增的整數(根據具體應用場景)。 一旦使用,便失效,不能重複使用。
示例: 區塊鏈挖礦中的 Nonce:
Nonce = 42
API 請求中的 Nonce:
{
"user_id": "12345",
"action": "login",
"nonce": "a1b2c3d4e5"
}
分散式事務與補償處理
在分散式系統中,操作可能涉及多個服務或資料庫,難以保證所有操作都在同一事務中完成。因此,經常使用以下設計模式來處理一致性問題:
分散式事務:通過 2PC或 3PC 來保證多個節點的一致性,但這種方式通常會降低性能,並且在高併發場景下不一定實用。
補償處理(Compensating Transactions):當某些操作失敗時,通過執行反向操作(補償操作)來撤銷已完成的部分。例如,若訂單支付成功但扣庫存失敗,則需要補償支付操作(退款)。
冪等性與分散式事務/補償處理的關聯
在分散式系統中,冪等性和補償處理經常結合使用,因為它們可以共同解決以下問題:
(1)重試機制中的冪等性 在分布式事務中,網絡波動可能導致某些操作需要重試。如果操作具有冪等性,多次執行不會對系統狀態造成影響,這樣可以簡化重試邏輯,避免數據錯亂。
例如:
在支付服務中,當支付請求因網絡超時被重試時,冪等性可以保證多次支付請求只會成功一次。
(2)補償操作的冪等性 補償操作本身也需要冪等性,因為補償可能因網絡問題或重試機制被多次執行。如果補償操作不是冪等的,可能會導致系統狀態不一致。
例如:
若退款操作被多次執行,可能導致多次退款給用戶,這是不可接受的。通過冪等性,系統可以保證只退款一次。
(3)分布式事務的簡化 在一些場景下,冪等性可以減少對分散式事務的依賴。例如,當操作本身具有冪等性時,系統可以允許某些操作失敗後重試,而不必執行複雜的分散式事務協調。
實現冪等性的常見方法
在設計分散式系統時,為了實現冪等性,通常會採用以下方法:
- 冪等欄位:為每個請求生成冪等欄位,服務端根據冪等欄位判斷請求是否已處理過。
- 樂觀鎖:通過版本號控制更新操作,避免多次執行導致數據錯亂。
- 狀態機檢查:在執行操作前檢查當前狀態,確保操作是否符合預期。
- 資料去重複:在資料層面設計去重機制,避免重複執行對數據造成影響。
總結
冪等性是分散式系統中處理網路retry 與防止重複執行操作的關鍵. 對於複雜場景可以結合分散式事務設計與補償處理.






