「解決方案」SpringBoot項目中如何解決併發導致的重複提交問題


「解決方案」SpringBoot項目中如何解決併發導致的重複提交問題


本文前篇是對場景的分析,後篇會有解決方案,讀完本篇你將可以僅僅使用兩個註解即可解決併發重複提交問題。

可以直接看方案四,直接讀推薦解決方案。

場景分析

重複提交問題是一個老生常談的問題,項目中經常會遇到這種情況,這種情況在查詢類接口其實也沒有太大問題,但是如果是在設計修改數據的接口就有會嚴重問題,但是這種情況並也不難處理,因為我們的代碼最少會做一個冪等判斷,即會先有一個查詢動作,查詢不到才會放行。但是難就難在假如說是併發加重複提交這種場景就很難處理。這個時候就不得不去思考新的解決方案。


解決方案

方案一、

通過數據庫唯一索引來解決,即在數據庫創建一張唯一表,在每次數據請求時候將唯一鍵作為數據插入這張唯一表中,正常情況是可以插入成功的,當出現重複提交情況就會異常提示。

缺點

  1. 數據庫性能問題,因為每次操作都設計到數據庫的一次插入動作,所以可能會有性能問題
  2. 數據只有一次處理機會,當第一次處理失敗,第二次在進來就當重複給攔截了

方案二、

token令牌,後端提供一個生成令牌的接口,前端在每次進行數據訪問時候,先拿去token令牌,後端通過對token令牌的生命週期管控,來解決重複提交問題

缺點: 前後端改造大,後端要單獨維護一個接口,前端每次請求也要多調一個接口


總結

希望通過查詢+修改方式來解決併發和重複提交問題都是不現實的,因為不能保證查詢和修改是一個原子性操作,所以只要併發就很容易突破這種方式的防重邏輯。那麼如何解決這個問題呢? 其實就是保證防重邏輯的原子性操作。同樣也是兩種方案。

方案三、

類似於通過數據庫唯一索引這種方式,不同的是將數據庫換成內存緩存即項目裡定義一個Cache集合緩存可以用Guava的緩存框架,設置緩存時間和緩存數量來解決。不過也是有缺點的,缺點就是不滿足分佈式要求,當請求打到其他應用服務器就突破了這種情況。所以不建議使用這種方案。如果是單機器可以考慮。

方案四、

是對上一種方案的改進,通過Redis實現,每次請求都插入Redis數據庫中,並設置過期時間, 既能滿足性能需求,同時也滿足分佈式情況。同時Redis因為是單線程的所以也能保證原子性。綜上所述這種方案應該是最好的。

  • 滿足原子性
  • 滿足分佈式環境應用
  • 性能有保證
  • 支持重試(通過設置過期時間)
  • 偽代碼如下

    「解決方案」SpringBoot項目中如何解決併發導致的重複提交問題

    終結解決方案

    該方案是對上面方案四的一個實現,感興趣的同學一個start一下,然後拉下來看看實現原理。

    核心原理就是方案四中提的,通過攔截和自動配置無縫整合到SpringBoot項目中使用。


    「解決方案」SpringBoot項目中如何解決併發導致的重複提交問題

    官網地址: https://tomato.springlearn.cn/


    使用方式


    「解決方案」SpringBoot項目中如何解決併發導致的重複提交問題

    如何判斷是否引用成功

    當出現tomato Logo即說明啟用成功

    「解決方案」SpringBoot項目中如何解決併發導致的重複提交問題


    「解決方案」SpringBoot項目中如何解決併發導致的重複提交問題


    感興趣的同學可以學習一下代碼,提出任何問題小編都會第一時間回覆。一起探討學習。

    接下來小編會圍繞Redis做更多的實戰分享目前定下來的兩個議題是:


    1.基於Redis原子性操作實戰應用一之併發攔截 「Tomato」

    2.基於Redis原子性操作實戰應用二之防洪限流「Easy-Sentinel」

    這兩個議題其實實戰代碼都已經寫好了,只是還沒有總結成文,感興趣的同學可以先到github上拉去實戰代碼。Tomato就是解決併發導致的重複提交,而Easy-Sentinel會更高級一點,利用Redis+Lua腳本實現原子性操作,從而來達到防洪限流的能力。


    分享到:


    相關文章: