摘要:否則數據會出現不同步問題我使用的做分布式鎖管理,用注解事務管理。但是出現另外一個問題,鎖超時但是事務仍未提交。
最近開發一個小程序遇到一個需求需要實現分布式事務管理
業務需求用戶在使用小程序的過程中可以查看景點,對景點地區或者城市標記是否想去,那么需要統計一個地點被標記的人數,以及記錄某個用戶對某個地點是否標記為想去,用兩個表存儲數據,一個地點表記錄改地點被標記的次數,一個用戶意向表記錄某個用戶對某個地點是否標記為想去。由于可能有多個用戶同時標記一個地點,每個用戶在前端點擊想去按鈕之后,后臺接收到請求,從數據庫查詢某個城市的標記人數,再加1,然后更新到數據庫。從數據庫查詢標記人數,再加1,然后更新到數據庫這個過程數據庫數據必須加鎖,一次只能一個進程處理。否則數據會出現不同步問題
我使用的RedLock做分布式鎖管理,用spring注解事務管理。
在實現過程中遇到如下兩個映像深刻的問題:
1、分布式鎖與spring注解事務共用產生的問題
2、鎖在事務提交前超時問題
最初實現代碼如下:
public markScenicSpot(){ //設置鎖為destId RLock lock = redisson.getLock("Afanti_markScenicSpot_updateCountwantAndCountbeenLock_" + ID); //嘗試獲取鎖 long lockTimeOut = 30; //持有鎖超時時間 **boolean success = lock.tryLock(5, lockTimeOut, TimeUnit.SECONDS);** if (success) { try { //業務邏輯實現 }catch (Exception e){ throw e; } finally{ //釋放鎖 **lock.unlock();** } } else { log.error("獲取鎖失敗!更新失敗!"); throw new BizException(ErrorCodeEnum.PROCESS_DATA_ERROR); } }問題:高并發是鎖沒有生效
1、spring注解事務@Transactional和分布式鎖不能一起使用
這是因為@Transactional是通過方法是否拋出異常來判斷事務是否回滾還是提交,此時方法已經結束。但是我們必須在方法結束之前釋放鎖,
因此在釋放鎖之后,此時還沒提交,由于鎖已經釋放,其他進程可以獲得鎖,并從數據庫查詢地點標記數,但是此時前一個進程沒有提交數據。該進程查到的數據不是最新的數據。
這個問題我排查的時候花了很久,因為鎖釋放和提交事務之間只要幾毫秒的時間,之前一直以為這么短的時間不可能是這里的問題,有懷疑過但是自己又放棄了
盡管這個過程只要很短的時間(我實際測試過程中這個過程只要幾毫秒),但是高并發的情況還是會出問題。
由于不能使用注解事務,我改為手動事務管理,增加如下代碼。
public markScenicSpot(){ //設置鎖為destId RLock lock = redisson.getLock("Afanti_markScenicSpot_updateCountwantAndCountbeenLock_" + ID); //嘗試獲取鎖 long lockTimeOut = 30; //持有鎖超時時間 boolean success = lock.tryLock(5, lockTimeOut, TimeUnit.SECONDS); if(success){ **DefaultTransactionDefinition def = new DefaultTransactionDefinition(); def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED); // 事物隔離級別 TransactionStatus status = transactionManager.getTransaction(def); // 獲得事務狀態** try { //業務邏輯實現 //...... **//提交事務 transactionManager.commit(status);** }catch (Exception e){ **//回滾事務 transactionManager.rollback(status);** } finally{ //釋放鎖 lock.unlock(); } } else { log.error("獲取鎖失敗!更新失敗!"); throw new BizException(ErrorCodeEnum.PROCESS_DATA_ERROR); } }問題:鎖超時事物異常
1、鎖超時問題
在進行手動事務管理之后,解決的同步問題。但是出現另外一個問題,鎖超時但是事務仍未提交。由于此時當前進程鎖超時但是沒有提交,此時其他進程可以獲得鎖并從數據庫查詢目的地標記數,但是不是更新之后的數據,取得的數據有誤。
針對鎖超時的情況,只需要當前進程提交之前增加一個判斷,判斷是否超時,如果超時拋出異常退出即可。
增加如下代碼:
public markScenicSpot(){ //設置鎖為destId RLock lock = redisson.getLock("Afanti_markScenicSpot_updateCountwantAndCountbeenLock_" + ID); //嘗試獲取鎖 long lockTimeOut = 30; //持有鎖超時時間 boolean success = lock.tryLock(5, lockTimeOut, TimeUnit.SECONDS); **//獲取鎖時間 long getLockTime=System.currentTimeMillis();** if(success){ //事務管理 DefaultTransactionDefinition def = new DefaultTransactionDefinition(); def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED); // 事物隔離級別 TransactionStatus status = transactionManager.getTransaction(def); // 獲得事務狀態 try { //業務邏輯實現 //...... //提交事務,判斷鎖是否超時 **if(System.currentTimeMillis()-getLockTime總結 高并發情況下,分布式事務很容易出問題,要對各種情況分析是否可能出問題,并要對所有可能出問題的情況做充分的測試才能保證程序健壯。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/76878.html
摘要:它就是史上最簡單的教程第三篇服務消費者后端掘金上一篇文章,講述了通過去消費服務,這篇文章主要講述通過去消費服務。概覽和架構設計掘金技術征文后端掘金是基于的一整套實現微服務的框架。 Spring Boot 配置文件 – 在坑中實踐 - 后端 - 掘金作者:泥瓦匠鏈接:Spring Boot 配置文件 – 在坑中實踐版權歸作者所有,轉載請注明出處本文提綱一、自動配置二、自定義屬性三、ran...
摘要:所以悲觀鎖是限制其他線程,而樂觀鎖是限制自己,雖然他的名字有鎖,但是實際上不算上鎖,只是在最后操作的時候再判斷具體怎么操作。悲觀鎖和樂觀鎖比較悲觀鎖適合寫多讀少的場景。 最近在公司的業務上遇到了并發的問題,并且還是很常見的并發問題,算是低級的失誤了。由于公司業務相對比較復雜且不適合公開,在此用一個很常見的業務來還原一下場景,同時介紹悲觀鎖和樂觀鎖是如何解決這類并發問題的。 公司業務就是...
閱讀 974·2021-11-24 10:42
閱讀 3522·2021-11-19 11:34
閱讀 2659·2021-09-29 09:35
閱讀 2543·2021-09-09 09:33
閱讀 688·2021-07-26 23:38
閱讀 2532·2019-08-30 10:48
閱讀 1399·2019-08-28 18:07
閱讀 433·2019-08-26 13:44