Java鎖競爭導(dǎo)致sql慢日志原因分析
線上在同步用戶時,經(jīng)常出現(xiàn)簡單sql的慢日志。根據(jù)方法找到代碼,發(fā)現(xiàn)方法內(nèi)使用redisson進行鎖操作,waiTime和leaseTime都為3秒,數(shù)據(jù)庫操作比較簡單,只是一個簡單的用戶更新操作。代碼簡化后如下
@Override @Transactional(rollbackFor = Exception.class) public LiveMemberResponseDTO becomeStudentNotQueryOrder(LiveBecomeStudentForOrderRequestDTO requestDTO) { EduUserEntity user = eduUserService.syncLocalByAccount(requestDTO.getOrderAccountId(), UserRegisterChannel.LIVE_APPLY); EduUserEntity applyUser = eduUserService.syncLocalByAccount(requestDTO.getApplyAccountId(), UserRegisterChannel.LIVE_APPLY); ... ... ... }
調(diào)用了更新方法
@Override @Lock(waitTime = 10000, leaseTime = 3000, value = RedisConstant.USER_SYNC_LOCAL, key = "#accountId") public EduUserEntity syncLocalByAccount(String accountId, String mobile, String fullName, String source, UserRegisterChannel registerChannel) { EduUserEntity eduUserEntity = queryUsersByAccountId(accountId); boolean updateFlag = false; .... .... ....//判斷是否需要更新 if (updateFlag) { saveOrUpdate(eduUserEntity); } return eduUserEntity; }
由于這里事務(wù)里面嵌套了redis鎖,并且涉及到更新表,可能會有死鎖的情況。通過在獲取鎖的地方打上地址獲取到以下日志
可以看到[Thread-13]在等待了三秒后才獲取到redis,根據(jù)獲取鎖的時機,列出表格
事務(wù)A(Thread-13) | 事務(wù)B(Thread-14) |
---|---|
獲取redis鎖 | |
獲取db鎖 | |
釋放redis鎖 | |
獲取redis鎖 | |
嘗試獲取db鎖 | |
嘗試獲取redis鎖 | |
等待三秒 | |
redis鎖超時,釋放redis鎖 | |
成功獲取redis鎖 | |
完成業(yè)務(wù) | |
釋放redis和db鎖 | |
獲取db鎖 |
這是由于兩次更新user表過程中,使用了一個事務(wù)A,導(dǎo)致事務(wù)B來獲取db行鎖的時候,被事務(wù)A阻塞。但在被事務(wù)A阻塞前,已經(jīng)獲取到了redis鎖,所以導(dǎo)致事務(wù)A在獲取第二次更新的redis鎖的時候被阻塞,造成了死鎖。
最終導(dǎo)致,redis鎖超時,日志方面體現(xiàn)為慢sql,因為事務(wù)B的sql等待了三秒才拿到鎖。
這種情況是因為事務(wù)和redis鎖的嵌套導(dǎo)致,所以指定更新方法的事務(wù)傳播等級為PROPAGATION_REQUIRED_NEW,不管外層是否存著事務(wù),都開啟新事務(wù)。
修改后代碼
@Override @Lock(waitTime = 10000, leaseTime = 3000, value = RedisConstant.USER_SYNC_LOCAL, key = "#accountId") //這里開啟新事務(wù) @Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class) public EduUserEntity syncLocalByAccount(String accountId, String mobile, String fullName, String source, UserRegisterChannel registerChannel) { EduUserEntity eduUserEntity = queryUsersByAccountId(accountId); boolean updateFlag = false; .... .... ....//判斷是否需要更新 if (updateFlag) { saveOrUpdate(eduUserEntity); } return eduUserEntity; }
聲明式事務(wù)使用很簡單,可以自動幫我們進行事務(wù)的開啟、提交以及回滾等操作,但是事務(wù)的顆粒度是整個方法,無法進行精細化控制。在使用過程中要注意事務(wù)的范圍與其他中間件的交互,通過指定適當?shù)膫鞑サ燃墎磉_到效果。
到此這篇關(guān)于Java鎖競爭導(dǎo)致sql慢日志原因分析的文章就介紹到這了,更多相關(guān)Java sql慢日志內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java上傳文件錯誤java.lang.NoSuchMethodException的解決辦法
今天小編就為大家分享一篇關(guān)于Java上傳文件錯誤java.lang.NoSuchMethodException的解決辦法,小編覺得內(nèi)容挺不錯的,現(xiàn)在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧2019-01-01