冪等的實現方案

2021-09-13 11:41:03 字數 1386 閱讀 7064

在軟體系統的開發過程中,我們可能有如下需求:

建立業務訂單,一次業務請求只能建立乙個;

單個訂單請求呼叫支付介面,當遇到網路或系統故障請求重發,也應該只支付一次;

單個訂單完成時,給使用者傳送訊息應該只發一次;

等等很多情況下,都需要冪等的特性來支援。

冪等(idempotence)一詞原為數學上的概念,用乙個最直觀的數學式子表達為:

f(f(x)) = f(x)
對應到軟體開發領域,即為同樣的請求被執行一次與連續執行多次的效果是一樣的,伺服器的狀態也是一樣的,實際上就是介面的可重複呼叫(包括時間和空間上兩個維度)。

不是要求返回值完全相同,而且是指後續多餘的呼叫對系統的資料一致性不造成破壞。對於寫入類操作,如果第一次寫入是成功的,後續的寫入應該丟擲異常或者空操作,或者執行了寫入但是未對資料造成變化。對於讀取類操作,需要保證其實現上是真正的讀取,不能在讀操作中夾帶寫操作。

冪等性不能脫離業務來討論。

在不同的需求場景下,實現冪等的思路和方案也會不同,一般有如下通用方案:

這是樂觀鎖的一種實現,用於在資料庫併發訪問時的情況。當資料更新時需要去判斷版本號是否一致,如果不一致,則無法對資料進行更新。例如請求支付介面進行扣款時:

update table_name set deposit = deposit-#, version = version + 1 where orderid = # and version = #
這裡orderid可進一步設為主鍵或唯一索引,因為這樣是行鎖,否則更新操作時會鎖表。樂觀鎖在對已有資料進行更新時既能保證效率,又能保證冪等。

這是利用資料庫表單的特性來實現冪等。以訂單請求支付場景為例:

將訂單號orderid設為去重表的唯一索引,每次請求支付都根據訂單號向去重表中插入一條資料,只有插入成功才繼續執行支付操作,相當於在事務的開始階段加鎖。

考慮兩種失敗的情況:

insert去重表失敗,事務回滾,無任何影響;

insert去重表成功,支付業務操作失敗,事務回滾,刪除之前插入去重表的記錄,無任何影響;

以上兩種失敗的情況下,事務的冪等性是可以保持的,避免了單個訂單同時多次進行支付的情況。

下圖為該支付場景下的時序圖:

與去重表思路相同,只是將對資料庫的的訪問轉移到了對快取(如redis)的訪問,提高了效率。具體操作如下:

訂單發起支付請求,支付系統會去redis快取中查詢是否存在該訂單號orderid的key,如果不存在,則向redis增加key為訂單號,然後開始實際支付操作;如果查詢到存在該訂單號的key,則不進行實際支付操作。無論支付操作成功或失敗,在支付操作結果返回後,在快取中刪除該訂單號key。

冪等性的實現

1.生成key的方式 記得保證redis生成的key和刪除的key是成功的 看返回值 1 允許表單跳轉 這種情況比較容易,比如在列表中新增一條記錄,可以在列表頁面生成乙個key,放到redis中,同時在新增頁面時帶著這個key。等到提交時,把key也提交,後台根據key與redis中進行比較,有的話...

冪等性的實現

1.生成key的方式 記得保證redis生成的key和刪除的key是成功的 看返回值 1 允許表單跳轉 這種情況比較容易,比如在列表中新增一條記錄,可以在列表頁面生成乙個key,放到redis中,同時在新增頁面時帶著這個key。等到提交時,把key也提交,後台根據key與redis中進行比較,有的話...

樂觀鎖的冪等性方案

那麼為了使用樂觀鎖,我們首先修改t goods表,增加乙個version欄位,資料預設version值為1。update t goods set count count 1 version version 1 where good id and version 1 根據version版本,也就是在操...