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