如何保證接口冪等性
1、冪等性是什么?
接口冪等性
是指用戶對于同一操作發起的一次請求或者多次請求的結果是一致的,不會因為多次點擊而產生了不同的結果。
2、使用冪等性的場景有哪些?
- 頁面點擊保存按鈕時,不小心快速點了兩次,表中竟然產生了兩條重復的數據,只是id不一樣。
- 我們在項目中為了解決
接口超時
問題,通常會引入了重試機制
。第一次請求接口超時了,請求方沒能及時獲取返回結果(此時有可能已經成功了),為了避免返回錯誤的結果(這種情況不可能直接返回失敗吧?),于是會對該請求重試幾次,這樣也會產生重復的數據。 - 消息重復消費,在使用消息中間件來處理消息隊列,且手動 ack 確認消息被正常消費時。如果消費者突然斷開連接,那么已經執行了一半的消息會重新放回隊列。當消息被其他消費者重新消費時,如果沒有冪等性,就會導致消息重復消費時結果異常,如數據庫重復數據,數據庫數據沖突,資源重復等。
3、如何保證接口冪等性?
- 悲觀鎖
- 樂觀鎖(版本號)
- 唯一索引
- 分布式鎖
- 去重表
- token機制
4、詳解具體實現
4.1、insert前先select(無法處理高并發)
? 通常情況下,在保存數據的接口中,我們為了防止產生重復數據,一般會在insert
前,先根據name
或code
字段select
一下數據。如果該數據已存在,則執行update
操作,如果不存在,才執行 insert
操作。
該方案可能是我們平時在防止產生重復數據時,使用最多的方案。但是該方案不適用于并發場景,在并發場景中,要配合其他方案一起使用,否則同樣會產生重復數據。
4.2、insert前先select并配合版本號,支持高并發(樂觀鎖)
-
先根據id查詢用戶信息,包含version字段
-
根據id和version字段值作為where條件的參數,更新用戶信息,同時version+1
-
判斷操作影響行數,如果影響1行,則說明是一次請求,可以做其他數據操作。
-
如果影響0行,說明是重復請求,則直接返回成功。
4.3、token機制
通過token 機制實現接口的冪等性,這是一種比較通用性的實現方法。
示意圖如下:
具體流程步驟:
-
客戶端會先發送一個請求去獲取 token,服務端會生成一個全局唯一的 ID 作為 token 保存在 redis 中,同時把這個 ID 返回給客戶端。
-
客戶端第二次調用業務請求的時候必須攜帶這個 token。
-
服務端會校驗這個 token,如果校驗成功,則執行業務,并刪除 redis 中的 token。
-
如果校驗失敗,說明 redis 中已經沒有對應的 token,則表示重復操作,直接返回指定的結果給客戶端
4.5、去重表
這種實現方式是利用 mysql 唯一索引的特性。示意圖如下:
具體流程步驟:
-
建立一張去重表,其中某個字段需要建立唯一索引
-
客戶端去請求服務端,服務端會將這次請求的一些信息插入這張去重表中
-
因為表中某個字段帶有唯一索引,如果插入成功,證明表中沒有這次請求的信息,則執行后續的業務邏輯
-
如果插入失敗,則代表已經執行過當前請求,直接返回。
4.6、分布式鎖
這種實現方式是基于 SETNX 命令實現的。
SETNX key value:將 key 的值設為 value ,當且僅當 key 不存在。若給定的 key 已經存在,則 SETNX 不做任何動作。該命令在設置成功時返回 1,設置失敗時返回 0。示意圖如下:
具體流程步驟:
-
客戶端先請求服務端,會拿到一個能代表這次請求業務的唯一字段
-
將該字段以 SETNX 的方式存入 redis 中,并根據業務設置相應的超時時間
-
如果設置成功,證明這是第一次請求,則執行后續的業務邏輯
-
如果設置失敗,則代表已經執行過當前請求,直接返回