接口保證冪等性你學廢了嗎?

接口冪等性定義:無論一次或多次調用某個接口,對資源產生的副作用都是一致的。

簡單來說:用戶由于各種原因(網絡超時、前端重復點擊、消息重試等)對同一個接口發了多次請求,系統只能處理一次,不能因為多次請求導致數據錯誤(如扣款兩次、生成兩個訂單)。

方案一:Token 機制(適用于新增、提交類操作)

這是最經典的防重提交方案,尤其適合前端表單提交、支付下單等場景。

核心思想:客戶端先獲取一個服務器頒發的唯一令牌(Token),提交請求時必須帶上這個Token。服務器處理請求后,使該Token失效。

流程:

  1. 獲取Token:客戶端在發起業務請求前,先調用一個接口獲取一個全局唯一的Token(通常存于Redis,并設置較短的有效期)。
  2. 提交請求:客戶端帶著業務參數和這個Token發起業務請求。
  3. 校驗Token:
    服務器端(通常通過AOP或Filter實現)檢查Redis中是否存在該Token。
    存在:執行業務邏輯,然后刪除Redis中的Token。
    不存在:說明該請求已被處理過,直接返回重復提交的錯誤信息。

代碼示例(AOP實現):

// 1. 自定義一個冪等性注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Idempotent {int expireTime() default 60; // Token有效期,秒
}// 2. 編寫Token創建接口
@RestController
public class TokenController {@Autowiredprivate StringRedisTemplate redisTemplate;@GetMapping("/token")public String getToken() {String token = UUID.randomUUID().toString();redisTemplate.opsForValue().set(token, "1", Duration.ofSeconds(60)); // 存入Redis,60秒過期return token;}
}// 3. 編寫AOP切面處理冪等性校驗
@Aspect
@Component
public class IdempotentAspect {@Autowiredprivate StringRedisTemplate redisTemplate;@Around("@annotation(idempotent)")public Object around(ProceedingJoinPoint joinPoint, Idempotent idempotent) throws Throwable {HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();String token = request.getHeader("X-Idempotent-Token"); // 從Header中獲取Tokenif (StringUtils.isEmpty(token)) {throw new RuntimeException("Token不存在");}// 核心邏輯:刪除Token(原子操作)。如果刪除成功,返回1,說明是第一次請求。Boolean isDeleted = redisTemplate.delete(token);if (Boolean.TRUE.equals(isDeleted)) {// 是第一次請求,放行return joinPoint.proceed();} else {// 刪除失敗,可能是Token已過期或被使用// 嘗試獲取Token,如果還能獲取到值,說明是重復請求(但還沒過期)。如果獲取不到,說明已過期。String tokenValue = redisTemplate.opsForValue().get(token);if (tokenValue != null) {throw new IdempotentException("請勿重復操作");} else {throw new IdempotentException("操作已過期,請刷新頁面后重試");}}}
}// 4. 在需要冪等性的接口上使用注解
@RestController
public class OrderController {@PostMapping("/createOrder")@Idempotent // 加上注解public String createOrder(@RequestBody Order order) {// 業務邏輯...return "訂單創建成功";}
}

優點:實現簡單,通用性強。
缺點:需要額外一次獲取Token的請求。

方案二:基于數據庫唯一索引(適用于插入操作)

核心思想:利用數據庫唯一索引的排他性,防止重復數據插入。

流程:

  1. 在數據表中為一個或多個字段建立唯一索引。這個“唯一鍵”可以是:
    業務主鍵(如訂單ID)
    組合字段(如:用戶ID + 業務類型 + 關聯ID)
  2. 插入數據時,如果唯一鍵重復,數據庫會拋出 DuplicateKeyException。
  3. 代碼中捕獲這個異常,返回“請勿重復操作”的提示。

示例:防止用戶重復創建訂單。

  • 在 order 表為 user_id 和 order_source_id(本次請求的唯一源ID)建立聯合唯一索引。
  • 每次創建訂單時,如果同一個用戶使用同一個源ID請求,第二次插入就會失敗。

優點:實現最簡單,無需額外編碼,依靠數據庫本身能力。
缺點:只適用于新增場景,不適用于更新、刪除操作。

注意:
這個唯一id并不是數據庫表的自增id,而是在發起請求前,生成一個全局唯一的業務ID,例如:order_id = 雪花算法生成的長整形ID。
將這個 order_id 隨請求一起傳到后端。
在數據庫的 order 表上,為 order_id 字段建立一個唯一索引。
插入時,如果兩個請求帶著同一個 order_id 到來,第二個請求就會因為唯一索引沖突而插入失敗。

方案三:悲觀鎖/樂觀鎖(適用于更新、扣減操作)

  1. 悲觀鎖(Pessimistic Lock)
    思想:“先取鎖,再操作”。認為并發沖突一定會發生,因此在操作數據時直接將其鎖住。
    實現:使用 SELECT … FOR UPDATE。
    場景:適用于寫操作非常頻繁,沖突概率極高的場景。要謹慎使用,容易導致性能瓶頸和死鎖。
  2. 樂觀鎖(Optimistic Lock) - 更推薦
    思想:“先操作,再驗證”。認為沖突不常發生,通過版本號(Version)或狀態機來保證。
    實現:
    在表中增加一個 version 字段。
    更新數據時,將 version 作為條件:UPDATE table SET value = new_value, version = version + 1 WHERE id = #{id} AND version = #{old_version}。
  3. 檢查 executeUpdate() 返回的影響行數:
    如果為 1:更新成功,是第一次請求。
    如果為 0:說明version已被其他請求修改過,是重復請求或沖突請求,操作失敗。

示例(余額扣款):

UPDATE account SET balance = balance - 100,version = version + 1
WHERE user_id = 123 
AND version = 5; -- 舊的版本號
--通過在一次事務內,先執行 SELECT 語句查詢出來最新的版本號再進行更新操作。

優點(樂觀鎖):性能高,避免數據庫鎖競爭。
缺點:需要修改表結構,失敗后需要重試或告知客戶端。

方案四:狀態機約束(適用于有狀態流轉的業務)

核心思想: 很多業務數據都有明確的狀態流轉(如:訂單狀態:0待支付->1已支付->2已發貨)。只有在特定狀態下,操作才是允許的。
流程:
執行更新操作時,不僅以ID為條件,還必須加上當前狀態作為條件。
如果狀態不符合預期,則更新失敗,說明請求無效或已處理過。

示例(支付回調接口):

-- 只有狀態為“待支付”的訂單才能被更新為“已支付”
UPDATE orders SET status = '已支付' WHERE order_id = '123' AND status = '待支付';

優點:業務邏輯本身自帶的冪等性,無需額外組件。
缺點:僅適用于有狀態變化的業務。

總結與選擇

方案適用場景優點缺點
Token 機制通用,尤其前端提交、創建操作通用性強,可靠性高需額外接口,多一次交互
唯一索引數據插入類操作實現最簡單,絕對可靠僅限插入操作
樂觀鎖數據更新、扣減庫存類操作性能好,避免鎖競爭需修改表結構,增加version字段
狀態機有明確狀態流轉的業務(訂單、流程)天然冪等,符合業務邏輯局限性較強
悲觀鎖極高并發寫場景(少用)保證強一致性性能差,易死鎖

在金融項目中如何選擇?
薪酬計算觸發接口:可用 Token機制 或基于業務ID的唯一索引(如 calculation_id),防止重復觸發計算。
獎金發放、余額扣減接口:樂觀鎖是最佳選擇,保證金額不會重復扣減。
訂單、流程狀態變更:狀態機約束是必須的,例如“只有未支付的訂單才能支付”。
最佳實踐: 通常會將多種方案組合使用。例如,先用 Token 機制 防重,在業務邏輯內再用 樂觀鎖 或 狀態機 做最終保障,形成雙保險。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/921126.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/921126.shtml
英文地址,請注明出處:http://en.pswp.cn/news/921126.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

入行FPGA選擇國企、私企還是外企?

不少人想要轉行FPGA,但不知道該如何選擇公司?下面就來為大家盤點一下FPGA大廠的薪資和工作情況,歡迎大家在評論區補充。一、老牌巨頭在 FPGA設計 領域深耕許久,流程完善、技術扎實,公司各項制度都很完善,前…

考研總結,25考研京區上岸總結(踩坑和建議)

我的本科是一所普通的雙非,其實,從我第一天入學時候,我就想走出去,開學給我帶來的更多是失望(感覺自己高考太差勁了),是不甘心(自己一定可以去更好的地方)。我在等一次機…

基于數據挖掘的當代不孕癥醫案證治規律研究

標題:基于數據挖掘的當代不孕癥醫案證治規律研究內容:1.摘要 背景:隨著現代生活方式的改變,不孕癥的發病率呈上升趨勢,為探索有效的中醫證治規律,數據挖掘技術為其提供了新的途徑。目的:運用數據挖掘方法研究當代不孕癥…

《sklearn機器學習》——調整估計器的超參數

GridSearchCV 詳解:網格搜索與超參數優化 GridSearchCV 是 scikit-learn 中用于超參數調優的核心工具之一。它通過系統地遍歷用戶指定的參數組合,使用交叉驗證評估每種組合的性能,最終選擇并返回表現最優的參數配置。這種方法被稱為網格搜索&…

一站式可視化運維:解鎖時序數據庫 TDengine 的正確打開方式

小T導讀:運維數據庫到底有多復雜?從系統部署到數據接入,從權限配置到監控告警,動輒涉及命令行、腳本和各種文檔查找,一不留神就可能“翻車”。為了讓 TDengine 用戶輕松應對這些挑戰,我們推出了《TDengine …

多線程同步安全機制

目錄 以性能換安全 1.synchronized 同步 (1)不同的對象競爭同一個資源(鎖得住) (2)不同的對象競爭不同的資源(鎖不住) (3)單例模式加鎖 synchronized …

多路復用 I/O 函數——`select`函數

好的&#xff0c;我們以 Linux 中經典的多路復用 I/O 函數——select 為例&#xff0c;進行一次完整、深入且包含全部代碼的解析。 <摘要> select 是 Unix/Linux 系統中傳統的多路復用 I/O 系統調用。它允許一個程序同時監視多個文件描述符&#xff08;通常是套接字&…

嵌入式碎片知識總結(二)

1.repo的一個問題&#xff1a;repo init -u ssh://shchengerrit.bouffalolab.com:29418/bouffalo/manifest/bouffalo_sdk -b master -m allchips-internal.xml /usr/bin/repo:681: DeprecationWarning: datetime.datetime.utcnow() is deprecated and scheduled for removal in…

java中二維數組筆記

課程鏈接:黑馬程序員java零基礎[上] 1.二維數組的內存分布 在 Java 中&#xff0c;二維數組并不是一整塊連續的二維空間&#xff0c;而是數組的數組。具體而言,在聲明一個二維數組&#xff1a;如int[][] arr new int[2][3];時&#xff0c;內存中會發生如下: 1.1 棧上的引用變…

系統架構設計師備考第13天——計算機語言-多媒體

一、多媒體基礎概念媒體的分類 感覺媒體&#xff1a;人類感官直接接收的信息形式&#xff08;如聲音、圖像&#xff09;。表示媒體&#xff1a;信息的數字化表示&#xff08;如JPEG圖像、MP3音頻&#xff09;。顯示媒體&#xff1a;輸入/輸出設備&#xff08;如鍵盤、顯示器&am…

指針高級(1)

1.指針的運算2.指針運算有意義的操作和無意義的操作、#include <stdio.h> int main() {//前提條件&#xff1a;保證內存空間是連續的//數組int arr[] { 1,2,3,4,5,6,7,8,9,10 };//獲取0索引的內存地址int* p1 &arr[0];//通過內存地址&#xff08;指針P&#xff09;…

【可信數據空間-Trusted Data Space綜合設計方案】

可信數據空間-Trusted Data Space綜合設計方案 一.簡介與核心概念 1.什么是可信數據空間 2.核心特征 3.主要應用場景 二、 產品設計 1. 產品定位 2. 目標用戶 3. 核心功能模塊 a. 身份與訪問管理 b. 數據目錄與服務發現 c. 策略執行與合約管理 d. 數據連接與計算 e. 審計與溯源…

技術方案之Mysql部署架構

一、序言在后端系統中&#xff0c;MySQL 作為最常用的關系型數據庫&#xff0c;其部署架構直接決定了業務的穩定性、可用性和擴展性。你是否遇到過這些問題&#xff1a;單機 MySQL 突然宕機導致業務中斷幾小時&#xff1f;高峰期數據庫壓力過大&#xff0c;查詢延遲飆升影響用戶…

js語言編寫科技風格博客網站-詳細源碼

<!-- 科技風格博客網站完整源碼 --> <!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <ti…

AI如何理解PDF中的表格和圖片?

AI的重要性已滲透到社會、經濟、科技、生活等幾乎所有領域&#xff0c;其核心價值在于突破人類能力的物理與認知邊界&#xff0c;通過數據驅動的自動化、智能化與優化&#xff0c;解決復雜問題、提升效率并創造全新可能性。從宏觀的產業變革到微觀的個人生活&#xff0c;AI 正在…

Graphpad Prism 實戰教程(一):小鼠體重變化曲線繪制全流程(含數據處理與圖表美化)

在藥理實驗、動物模型構建等科研場景中,小鼠體重變化數據是評估實驗干預效果(如藥物安全性、疾病進展影響)的核心指標之一。將零散的體重數據轉化為直觀的折線圖,不僅能清晰呈現體重隨時間的波動趨勢,更是后續結果解讀與論文圖表呈現的關鍵步驟。本文將從 Excel 數據整理開…

計算機視覺(六):腐蝕操作

腐蝕&#xff08;Erosion&#xff09;是計算機視覺和圖像處理中一種基礎且至關重要的形態學操作。它與膨脹&#xff08;Dilation&#xff09;互為對偶&#xff0c;共同構成了形態學處理的基石。腐蝕操作主要用于縮小前景物體的面積&#xff0c;去除圖像中的噪聲&#xff0c;以及…

AI隨筆番外 · 貓貓狐狐的尾巴式技術分享

&#x1f380;【開場 咱才不是偷懶寫博客】&#x1f43e;貓貓趴在鍵盤邊&#xff0c;耳朵一抖一抖&#xff1a;“嗚嗚嗚……明明說好要寫技術總結&#xff0c;結果咱腦袋里全是尾巴……要不今天就水一篇隨意的 AI 技術分享算啦&#xff1f;”&#x1f98a;狐狐把書卷輕輕放在桌…

數據分析與挖掘工程師學習規劃

一、數學與統計學基礎概率論與數理統計隨機變量、概率分布&#xff08;正態分布、泊松分布等&#xff09;、大數定律、中心極限定理假設檢驗、置信區間、方差分析&#xff08;ANOVA&#xff09;、回歸分析貝葉斯定理及其在分類問題中的應用&#xff08;如樸素貝葉斯算法&#x…

(線上問題排查)4.CPU使用率飆升:從應急滅火到根因治理

目錄 從宏觀到微觀&#xff1a;CPU排查的“破案”流程 第一階段&#xff1a;應急響應——找到“誰”在搗亂 1. 全局視角&#xff1a;top命令的初窺 2. 進程內窺視&#xff1a;揪出問題線程 第二階段&#xff1a;深入分析——理解“為什么” 3. 線程堆棧分析&#xff1a;查…