天天看點

如何防止資料重複插入?

作者:泥瓦匠@bysocket.com
如何防止資料重複插入?

目錄

  1. 為啥要解決資料重複插入?
  2. 解決方案實戰
  3. 可落地小總結

一、為啥要解決資料重複插入?

問題起源,微信小程式抽風 wx.request() 重複請求伺服器送出資料。後端服務也很簡單,僞代碼如下:

發現資料庫會存在重複資料行,送出時間一模一樣。但業務需求是不能有多餘的 log 出現,這明顯是個問題。

問題是,重複請求導緻的資料重複插入。這問題造成的後果很明顯:

  • 資料備援,可能不單單多一條
  • 有些業務需求不能有多餘資料,造成服務問題

問題如圖所示:

如何防止資料重複插入?

解決方式:如何将 同請求 A,不執行插入,而是讀取前一個請求插入的資料并傳回。解決後流程應該如下:

如何防止資料重複插入?

二、解決方案實戰

1.單庫單表解決方案

  • 唯一索引 + 唯一字段
  • 幂等

上面說的那種業務場景:signlog 表會有 userid、signid、signtime 等。那麼每次簽到,每個人每天隻有一條簽到記錄。

資料庫層采取唯一索引的形式,保證資料記錄唯一性。即 UNIQUE 限制,UNIQUE 限制唯一辨別資料庫表中的每條記錄。另外,userid,signid,sign_time 三個組合适唯一字段。創表的僞代碼如下:

重點是 ​

​CONSTRAINT unique_sign_log UNIQUE(user_id,sign_id,sign_time)​

​。有個小問題,資料量大的時候,每條記錄都會有對應的唯一索引,比較耗資源。那麼這樣就行了嗎?答案是不行,服務不夠健壯。第一個請求插入成功,第二個請求直接報錯,Java 服務會抛出 ​

​DuplicateKeyException​

​ 。

簡單的幂等寫法操作即可,僞代碼如下:

的确,流量不是很大,也不算很高并發。重複寫問題,這樣處理即可。那大流量、高并發場景咋搞

2.分庫分表解決方案

流量大了後,單庫單表會演變成分庫分表。那麼基于單表的唯一索引形式,在碰到分表就無法保證呢,插入的地方可能是兩個分表 A1 和 A2。

解決思路:将資料的唯一性條件放到其他存儲,并進行鎖控制

還是上面的例子,每天,每次簽到,每個人隻有一條簽到記錄。那麼使用分布式鎖 Redis 的解決方案。大緻僞代碼如下:

a.加鎖

  • lockKey 最簡單的是 userid + signid + sign_time
  • expireTime 設定為一天

b.解鎖

c.幂等代碼加強

這個方案還是不是很成熟,大家參考下即可。

三、可落地小總結

解決方案實戰中,了解具體術。歸納如下:

  • 幂等:保證多次同意請求後結果一緻
  • 并發控制:單表唯一索引、分布式多表分布式鎖
  • 降級兜底方案:分布式鎖鎖失效 - 考慮樂觀鎖兜底

參考資料

  • 重複插入方案: http://www.bysocket.com/archives/2266
  • 《阿裡巴巴 Java 開發手冊》

以下專題教程也許您會有興趣

  • 《Spring Boot 2.x 系列教程》 https://www.bysocket.com/springboot
  • 《Java 核心系列教程》 https://www.bysocket.com/archives/2100​
由于能力有限,若有錯誤或者不當之處,還請大家批評指正,一起學習交流!

-The End-

号外:為讀者持續幾份最新教程,覆寫了 Spring Boot、Spring Cloud、微服務架構等。

擷取方式:下面關注公衆号,并回複 java 或 666 領取

最新整理:下面關注公衆号,并回複 webflux 或 888 領取《Spring Boot WebFlux 必會必知系列教程》

繼續閱讀