Java多執行緒:解決高併發環境下資料插入重複問題
1.背景描述
應用框架:Spring + SpringMVC + Hibernate
資料庫:Oracle11g
一家文學網站向我係統推多執行緒低併發推送資料,我這邊觀察日誌和資料庫,發現有一個作者被儲存了2次到資料庫中。按照程式的編寫邏輯,重複的資料是會被判斷出來不被儲存的。
2.原因分析
由於網路原因,客戶可能連續推送了兩條重複的資料,兩條資料時間間隔非常小,因此導致了我們的
if(使用者不存在)
{
xxxxx
儲存使用者到資料庫
}
else
{
重複推送,不採取任何措施
}
這個操作還沒有執行完畢,第二條擁有相同資料的執行緒已經進入並通過了if的檢驗,導致資料庫儲存了兩條相同的資料。後來我自己寫了個100併發的多執行緒測試程式,發現100條相同資料中有40條被插入到了資料庫裡!天啦嚕!!!因此確定了是多執行緒的併發導致了程式的判斷邏輯失效。
3.解決思路:
1) 在Author主表中對 身份證號 添加了唯一索引,現在Author主表不會出現重複資料了,如果連續推送客戶會收到一條推送失敗的提示資訊。
2) 但是AuthorOrg表(Author與AuthorOrg是一對多關係)依然會出現重複資料,想過新增siteId + userUniqueId的 聯合唯一索引來解決,但是想到work表也會出現同樣的問題,新增過多索引會導致DB佔用空間無限增大,因此不採用。
3) 考慮使用synchronized對方法新增同步鎖,但是這樣會導致其他正常資料的推送執行緒也被阻塞,影響效率。因此不採用。
4) 使用對資料庫新增行鎖,實驗發現還是會出現2條重複資料 分析: 理論上的結果應該是1條成功,149條失敗。 對資料庫的select語句新增行鎖必須作用於某條記錄,但是第一次報送時,資料庫中並沒有這條資料,因此行鎖根本沒有加上,導致第二條資料成功非同步使用select語句。 第一次報送成功以後,資料庫中有了這條資料,select語句成功的對這條記錄添加了行鎖,所以後邊不會出現重複資料。因此此法不可用。
5) 即想提高效率不對方法新增synchronized,又想保證資料準確性,最後使用synchronized(siteId + uid) 在Controller層加鎖(保證了只有重複資料被加鎖,在Controller使用的原因是因為事務會在Service呼叫完畢才被提交,我實驗過在Service同步,150併發會出現2條重複資料,因為事務還沒來得及提交)
測試結果:測試了3次150併發 不到一秒的時間全部返回,結果1條登記成功,149條返回該作者已登記。
下一步:
針對所有可能出現高併發問的介面進行調整。
4.提示
這種加同步鎖的方法在
負載均衡下的多臺應用伺服器會失效
!因為就算Spring保證了物件是單例的,但是多臺伺服器肯定是多個物件!因此synchronized將無效。解決方法是在資料庫層對該對接公司的唯一記錄加select鎖,這樣就能保證資料的不重複性,但是會降低該公司推送資料的效率(相當於逐條推送),但是公司與公司之間還是並行推送的。還有一個方法就是將業務邏輯寫入儲存過程,然後對儲存過程加鎖,這種方法太麻煩了,需求有變動就必須去修改儲存過程,但是效率要比前者高得多。
相關推薦
Java多執行緒:解決高併發環境下資料插入重複問題
1.背景描述 應用框架:Spring + SpringMVC + Hibernate 資料庫:Oracle11g 一家文學網站向我係統推多執行緒低併發推送資料,我這邊觀察日誌和資料庫,發現有
使用Redis中介軟體解決商品秒殺活動中出現的超賣問題(使用Java多執行緒模擬高併發環境)
開發十年,就只剩下這套架構體系了! >>>
總結-Java多執行緒與高併發簡記
1、什麼是多執行緒? 一個程序可以開啟多個執行緒,每個執行緒可以併發/並行執行不同任務。 2、Java多執行緒實現方式 2.1、繼承Thread類 2.2、實現Runnable介面方式實現多執行緒 2.3、使
java多執行緒與高併發庫應用(二)執行緒建立和定時任務Timer
1、建立執行緒的兩種方式, 通過start, 執行run方法。 第一種實現runnable, 定義類實現Runnable介面 重寫Runnable介面中的run方法 通過Thread建立執行緒物件 將Runnable介面的子類物件作為實際引數傳遞
Java多執行緒與高併發:java.util.concurrent包
面試官:你用過JUC的哪些工具類? 前面從基礎開始,到執行緒安全的實現、物件的釋出與共享,涉及到很多執行緒安全的類與工具,JDK1
一篇部落格帶你輕鬆應對java面試中的多執行緒與高併發
1. Java執行緒的建立方式 (1)繼承thread類 thread類本質是實現了runnable介面的一個例項,代表執行緒的一個例項。啟動執行緒的方式start方法。start是一個本地方法,執行後,執行run方法的程式碼。 (2)實現runnable
JAVA多執行緒(四) Executor併發框架向RabbitMQ推送訊息
github程式碼地址:https://github.com/showkawa/springBoot_2017/tree/master/spb-demo 假設一個需求使用者點選某個頁面,我們後臺需要向MQ推送信資訊 1,模擬的MQ服務,我這邊使用RabbitMQ (關於MQ 傳送和監聽訊息可以
多執行緒與高併發基礎知識
一、概覽 多執行緒主要知識點: 執行緒安全、執行緒封閉、執行緒排程、同步容器、併發容器、AQS、J.U.C 高併發解決方案: 擴容、快取、佇列、拆分、服務降級與熔斷、資料庫切庫、分庫分表 二、併發 概念: 同時擁有兩個或者多個執行緒,如果程式在單核處理器上執行
1.多執行緒-瞭解多執行緒與高併發
併發與並行的區別: 併發:兩個任務或者多個任務執行,多個任務交替執行 並行:兩個任務或者多個任務一起同時執行 例子: 一個CPU,去執行一個多執行緒任務。是不可能並行的,一個CPU只能執行一條命令,CPU會高速的切換執行緒任務去執行。這種情況下執行緒是併發的。一
多執行緒,高併發的情況下操作redis當中的資料,如何加鎖?
多個執行緒同時去操作Redis當中的資料,假如不加鎖的情況下,會出現資料重複的問題。假如需要每次都只有一條執行緒去操作Redis當中的資料,需要給操作加上鎖。 但是去網上一搜,網上給Redis加鎖的機制都是利用Redis的setnx自身的方法去加鎖,但是這樣
同步、非同步、多執行緒、高併發不再混淆!
高併發:“短時間內遇到大量操作請求”的情況。 多執行緒:多執行緒就是指一個程序中同時有多個執行緒正在執行。其目的就是當某個執行緒很耗時的時候使用多執行緒,可以在將耗時任務放在後臺繼續執行的同時,同時執行其他操作,達到提升效率,優化使用者體驗的效果。 多執行緒是完成高併發任
多執行緒與高併發基礎一(超發--悲觀鎖,樂觀鎖)
關鍵詞: 執行緒,同步,單例,高併發,高訪問,死鎖 一、大規模併發帶來的挑戰 在過去的工作中,我曾經面對過5w每秒的高併發秒殺功能,在這個過程中,整個Web系統遇到了很多的問題和挑戰。如果Web系統不做針對性的優化,會輕而易舉地陷入到異常狀態。我們現在一起來討論下
多執行緒與高併發程式設計之基礎知識(上)
前言 幾乎所有的程式設計師都知道,現代作業系統進行資源分配的最小單元是程序,而作業系統進行運算排程的最小單元是執行緒,其實,在Linux中執行緒也可以看作是一種輕量級的程序,那麼執行緒是包含於程序之中的,是程序中實際的運作單位;同一程序中的多個執行緒共用同一塊
java多執行緒-專題-聊聊併發(二)Java SE1.6中的Synchronized
1 引言 在多執行緒併發程式設計中Synchronized一直是元老級角色,很多人都會稱呼它為重量級鎖,但是隨著Java SE1.6對Synchronized進行了各種優化之後,有些情況下它並不那麼重了,本文詳細介紹了Java SE1.6中為了減少獲得鎖和釋放鎖帶來的效
多執行緒與高併發程式設計進階(一)
前言: 使用多執行緒的目的: 充分利用CPU資源,提高程式執行速度 使用多執行緒面臨的挑戰: 上下文切換、死鎖、計算機軟硬體資源的限制等問題 結論: 不是一味地開啟執行緒就能夠讓程式最大限度地併發執行,以及提升執行速度,想利用多執行緒提升程式執行速度需要結合實際
多執行緒與高併發
1、傳統執行緒技術:Thread –> run() Runnable 2、傳統定時器:Timer –> schedule()
java多執行緒解決同步問題的幾種方式、原理和程式碼
生產者類: publicclassProducerextendsThread{// 每次生產的產品數量privateint num;// 所在放置的倉庫privateStorage storage;// 建構函式,設定倉庫publicProducer(Storage storage){this.stora
多執行緒WEB高併發壓力測試軟體JMeter
一、 Apache JMeter工具 1)簡介 JMeter——一個100%的純java桌面應用,它是 Apache組織的開放原始碼專案,它是功能和效能測試的工具。JMeter可以用於測試靜態或者動態資源的效能(檔案、Servlets、Perl指令碼、java物
多執行緒解決socket併發問題
概念 這篇部落格我們利用多執行緒解決伺服器併發問題 程序是資源分配最小的單位,執行緒是CPU排程的最小單位 多程序與多執行緒比較(下圖取自網路,原作者不詳) 對比維度 多程序 多執行緒 總結 資料共享、同步 資料共享複雜,
多執行緒模擬高併發情況redis 與資料庫快取不一致
import com.mysql.jdbc.Connection; import entity.User; import org.junit.Test; import redis.clients.jedis.Jedis; import java.sql.*; import