在軟件系統的開發過程當中,咱們可能有以下需求:redis
等等不少狀況下,都須要冪等的特性來支持。數據庫
冪等(idempotence)一詞原爲數學上的概念,用一個最直觀的數學式子表達爲:緩存
f(f(x)) = f(x)
對應到軟件開發領域,即爲一樣的請求被執行一次與連續執行屢次的效果是同樣的,服務器的狀態也是同樣的,實際上就是接口的可重複調用
(包括時間和空間上兩個維度)。
不是要求返回值徹底相同,並且是指後續多餘的調用對系統的數據一致性不形成破壞。對於寫入類操做,若是第一次寫入是成功的,後續的寫入應該拋出異常或者空操做,或者執行了寫入可是未對數據形成變化。對於讀取類操做,須要保證其實現上是真正的讀取,不能在讀操做中夾帶寫操做。服務器
冪等性不能脫離業務來討論。
在不一樣的需求場景下,實現冪等的思路和方案也會不一樣,通常有以下通用方案:網絡
這是樂觀鎖的一種實現,用於在數據庫併發訪問時的狀況。當數據更新時須要去判斷版本號是否一致,若是不一致,則沒法對數據進行更新。例如請求支付接口進行扣款時:併發
update table_name set deposit = deposit-#{payment}, version = version + 1 where orderId = #{orderId} and version = #{version}
這裏orderId可進一步設爲主鍵或惟一索引,由於這樣是行鎖,不然更新操做時會鎖表。樂觀鎖在對已有數據進行更新時既能保證效率,又能保證冪等。分佈式
這是利用數據庫表單的特性來實現冪等。以訂單請求支付場景爲例:
將訂單號orderId設爲去重表的惟一索引,每次請求支付都根據訂單號向去重表中插入一條數據,只有插入成功才繼續執行支付操做,至關於在事務的開始階段加鎖。
考慮兩種失敗的狀況:ide
以上兩種失敗的狀況下,事務的冪等性是能夠保持的,避免了單個訂單同時屢次進行支付的狀況。
下圖爲該支付場景下的時序圖:spa
與去重表思路相同,只是將對數據庫的的訪問轉移到了對緩存(如redis)的訪問,提升了效率。具體操做以下:
訂單發起支付請求,支付系統會去redis緩存中查詢是否存在該訂單號orderId的key,若是不存在,則向redis增長key爲訂單號,而後開始實際支付操做;若是查詢到存在該訂單號的key,則不進行實際支付操做。不管支付操做成功或失敗,在支付操做結果返回後,在緩存中刪除該訂單號key。code