透過sql查詢
select * from information_schema.innodb_trx;
我們發現是更新表sys_sn_rule導致的,那麼我們理一下程式碼,看看為什麼會出現LOCK_WAIT。
原因排查透過檢視介面呼叫,我們定位到一個方法上,這裡我將方法簡化。簡化後的程式碼如下:
@Override@Transactional(rollbackFor = Exception.class)public Result funA() { //更新表table1; funB(); ...}public void funB() { //更新表table1; ....}
問題就出在,funA呼叫了funB,而兩個方法同時操作了表table1上的同一條資料。這裡會有兩個事務,在更新資料時,會產生兩個事務都在互相等待對方關閉事務,從而到時死鎖。我們來作圖說明下:
如上圖,執行funA時,會執行更新表table1,更新表前會開啟事務A,更新表時會給這行資料上鎖(為了保護資料的一致性)。接下來呼叫funB,開啟事務B,更新表table1,因為表table1的這行已經鎖住了,所以事務B中需要等鎖釋放才能繼續執行。但是事務A要想關閉,需要等funA執行完才能關閉。而funA中呼叫了funB,funB要等待table1釋放鎖才能執行完。這樣就導致了死迴圈。
關於資料庫加鎖的知識,可以看看這篇文章:
解決死鎖之路 - 學習事務與隔離級別 - aneasystone's blog
(https://www.aneasystone.com/archives/2017/10/solving-dead-locks-one.html)
解決辦法我們可以採用多執行緒解決:
@Override@Transactional(rollbackFor = Exception.class)public Result funA() { //更新表table1; taskExecutor.execute(() -> { try { Thread.sleep(5 * 1000); funB(); } catch (InterruptedException e) { e.printStackTrace(); } }); ...}public void funB() { //更新表table1; ....}
採用多執行緒,兩個事務就分別在兩個不同的執行緒裡面了,就不會出現迴圈等待的情況。
程式碼修改後,測試順利透過。
最新評論