如何保證分布式情況下的冪等性
關于這個分布式服務的冪等性,這是在使用分布式服務的時候會經常遇到的問題,比如,重復提交的問題。而冪等性,就是為了解決問題存在的一個概念了。
什么是冪等
冪等(idempotent、idempotence)是?個數學與計算機學概念,常?于抽象代數中。
在編程中?個冪等操作的特點是其任意多次執?所產?的影響均與?次執?的影響相同。冪等函數,或 冪等?法,是指可以使?相同參數重復執?,并能獲得相同結果的函數。這些函數不會影響系統狀態, 也不?擔?重復執?會對系統造成改變。例如,“setTrue()”函數就是?個冪等函數,?論多次執?,其結 果都是?樣的,更復雜的操作冪等保證是利?唯?交易號(流?號)實現.
接?冪等性就是?戶對于同?操作發起的?次請求或者多次請求的結果是?致的,不會因為多次點擊? 產?了副作?。
什么是接口的冪等性
在HTTP/1.1中,對冪等性進行了定義。它描述了一次和多次請求某一個資源對于資源本身應該具有同樣的結果(網絡超時等問題除外),即第一次請求的時候對資源產生了副作用,但是以后的多次請求都不會再對資源產生副作用。這里的副作用是不會對結果產生破壞或者產生不可預料的結果。也就是說,其任意多次執行對資源本身所產生的影響均與一次執行的影響相同。
不能保證冪等性的操作
- 前端重復提交表單:在填寫一些表格時候,用戶填寫完成提交,很多時候會因網絡波動沒有及時對用戶做出提交成功響應,致使用戶認為沒有成功提交,然后一直點提交按鈕,這時就會發生重復提交表單請求。
- 用戶惡意進行刷單:例如在實現用戶投票這種功能時,如果用戶針對一個用戶進行重復提交投票,這樣會導致接口接收到用戶重復提交的投票信息,這樣會使投票結果與事實嚴重不符。
- 接口超時重復提交:很多時候 HTTP 客戶端工具都默認開啟超時重試的機制,尤其是第三方調用接口時候,為了防止網絡波動超時等造成的請求失敗,都會添加重試機制,導致一個請求提交多次。
- 消息進行重復消費:當使用 MQ 消息中間件時候,如果發生消息中間件出現錯誤未及時提交消費信息,導致發生重復消費。
如果放到數據庫的操作層面,那么就有很多操作需要去保證冪等性了。
- A: 查詢操作
查詢對于結果是不會有改變的,查詢?次和查詢多次,在數據不變的情況下,查詢結果是?樣的。 select是天然的冪等操作
- B: 刪除操作
刪除?次和多次刪除都是把數據刪除。(注意可能返回結果不?樣,刪除的數據不存在,返回0,刪除 的數據多條,返回結果多個,在不考慮返回結果的情況下,刪除操作也是具有冪等性的)
- C: 更新操作
修改在?多場景下結果?樣,但是如果是增量修改是需要保證冪等性的,如下例?:
把表中id為XXX的記錄的A字段值設置為1,這種操作不管執?多少次都是冪等的
把表中id為XXX的記錄的A字段值增加1,這種操作就不是冪等的
- D: 新增操作
增加在重復提交的場景下會出現冪等性問題,如以上的?付問題
如何實現冪等性
其實實現冪等性的方案有不少,但是呢,這就得需要你根據不同的業務場景去選擇合適的方式了。
實現方式一
數據庫唯一主鍵
數據庫唯一主鍵的實現主要是利用數據庫中主鍵唯一約束的特性,一般來說唯一主鍵比較適用于“插入”時的冪等性,其能保證一張表中只能存在一條帶該唯一主鍵的記錄。
使用數據庫唯一主鍵完成冪等性時需要注意的是,該主鍵一般來說并不是使用數據庫中自增主鍵,而是使用分布式 ID 充當主鍵(可以參考 Java 中分布式 ID 的設計方案 這篇文章),這樣才能能保證在分布式環境下 ID 的全局唯一性。
而實際上生成這個主鍵的方式就是在當請求的時候后,生成分布式唯一ID,然后當做主鍵插入數據庫,來保證唯一即可。
實現方式二
Token機制
Token機制,實際上也可以稱為 Token 令牌
- 服務端提供了發送token的接?。我們在分析業務的時候,哪些業務是存在冪等問題的,就必須在 執?業務前,先去獲取token,服務器會把token保存到redis中。(微服務肯定是分布式了,如果 單機就適?jvm緩存)。
- 然后調?業務接?請求時,把token攜帶過去,?般放在請求頭部。
- 服務器判斷token是否存在redis中,存在表示第?次請求,這時把redis中的token刪除,繼續執?業務。
- 如果判斷token不存在redis中,就表示是重復操作,直接返回重復標記給client,這樣就保證了業務代碼,不被重復執?。
實現方式三
數據庫樂觀鎖
數據庫樂觀鎖方案一般只能適用于執行“更新操作”的過程,我們可以提前在對應的數據表中多添加一個字段,充當當前數據的版本標識。這樣每次對該數據庫該表的這條數據執行更新時,都會將該版本標識作為一個條件,值為上次待更新數據中的版本標識的值。
為了每次執行更新時防止重復更新,確定更新的一定是要更新的內容,我們通常都會添加一個 version 字段記錄當前的記錄版本,這樣在更新時候將該值帶上,那么只要執行更新操作就能確定一定更新的是某個對應版本下的信息。
這樣的話,有了 version 的存在,這樣就能保住更新的冪等,多次更新對結果不會產生影響。
你還了解有哪些實現冪等性操作的方式呢?