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

軟件編程指南 發佈 2020-01-17T04:42:21+00:00

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



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

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

場景分析

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


解決方案

方案一、

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

缺點

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

方案二、

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

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


總結

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

方案三、

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

方案四、

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

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

偽代碼如下

終結解決方案

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

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


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


使用方式


如何判斷是否引用成功

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



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

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


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

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

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

關鍵字: