02.26 點一下按鈕調兩次接口?淺談接口設計的冪等性

在單體架構時代,就存在著接口冪等性的問題,只不過到了分佈式、高併發的場景之後,接口冪等性的問題會更加明顯。

冪等性的概念

那麼什麼是冪等性呢?

當用戶對同一操作請求了一次或者多次,最終的結果是一致的,並不會因為多次請求產生副作用;比如同一個訂單支付了兩次,最後應該只扣客戶一次錢。

查詢和刪除:查詢具有天然的冪等性,在數據不變的前提下,相同查詢條件查詢一次和查詢多次的結果都是一樣的;刪除也一樣,相同的條件刪除一次和刪除多次,可能刪除的數據量不一樣,但是數據庫中的數據不會因為執行了多次刪除而不同。

新增和修改:如果不做冪等性處理,可能就會產生問題;執行多次新增操作,可能會導致一模一樣的數據產生了多條(主鍵自動生成);修改操作,如果只是把某些字段更新成固定的值,不會有冪等性問題,但是如果新值要在舊值上做處理做計算,如增加多少、減少多少,那麼多次執行的結果就會有差異。

那麼為了保證接口的冪等性,有哪些方法呢?


保證冪等性的解決方案

1. 唯一索引

使用唯一索引,可以有效的防止新增髒數據:當表中存在唯一索引的時候,併發新增相同數據的時候就會報錯,不過這在單庫單表的時候才有效,如果項目數據量很大,採用了分庫分表的策略,就不能再通過數據庫的唯一性索引來解決冪等性的問題了。


2. 悲觀鎖

獲取數據的時候加鎖獲取;

<code>select * from table where col='xxx' for update;/<code>

這裡要注意, where 條件中的字段必須是主鍵或者有索引,否則會鎖全表。

3. 分佈式鎖

在業務系統執行插入或更新操作的時候,先要獲取分佈式鎖,然後做操作,之後釋放鎖;分佈式鎖保證在一個時間內,只會有一個線程對數據進行操作。


4. 全局唯一 ID

每次請求,都生成一個全局的唯一 ID,接口調用的時候攜帶者這個 ID,而業務操作方在之執行前判斷這個 ID 是否已經在本地存在,如果不存在,則執行交易後記錄 ID(存到數據庫或Redis中,表示該交易已經執行);如果已經存在,表示交易已經執行過了,不能再次執行。

許多分佈式架構中,生成全局唯一 ID 都會被作為一個基礎的微服務,當然這個服務的可靠性要求極高,或者可以使用全局唯一 ID 算法,由每個應用自己生成。不過總的來說,引入全局唯一 ID 這個方案,實現起來還是非常繁瑣的。


點一下按鈕調兩次接口?淺談接口設計的冪等性


5. 數據版本號

這個方案算是樂觀鎖的一種;在數據中增加版本號的概念,那麼在做數據修改,把當前數據的版本號帶上,修改的時候要按照版本號判斷數據是否發生過更改。如果沒有發生過更改,則執行業務操作,並更新版本號(這種方法適合在更新的場景中)。


下面的代碼,意會一下:

<code>updateDate(Object obj , int version);update set obj , version+1 where ... and version = 入參version;/<code>


6. 業務狀態

有些業務流程,每一步都是有狀態的,比如網上購物可能會有:訂單創建、付款、發貨,那麼付款之前保單狀態為“待付款”,付款之後可以將保單的狀態修改為“待發貨”;那麼如果發起重複扣款的話,第二次扣款的時候保單狀態已經變化了,就會扣款失敗。


點一下按鈕調兩次接口?淺談接口設計的冪等性


7. 去重表

如果業務中有唯一性的標識時,可以使用去重表,把這個唯一性的表示保存到去重表中,如果重複插入,那麼會被校驗住。

比如上面的場景,一個訂單隻會付款一次,那麼在付款的時候,把訂單號作為唯一性的標識,保存到去重表中,可以保證付款操作只會發生一次;這個方法也用到了唯一 ID,不過和全局唯一 ID 不同,這個唯一 ID 是針對具體業務的。

點一下按鈕調兩次接口?淺談接口設計的冪等性


分享到:


相關文章: