1. 程式人生 > >記錄工作遇到的死鎖問題(Lock wait timeout exceeded; try restarting transaction)

記錄工作遇到的死鎖問題(Lock wait timeout exceeded; try restarting transaction)

1.問題背景

剛來新公司不久,對業務還不太熟悉,所以領導先安排我維護原有系統。大概介紹下專案背景,專案分為核心業務部分在專案A中,與第三方互動的業務在專案B中,前端發起請求呼叫A專案介面,並在A專案中呼叫B專案介面,並在B專案中呼叫第三方獲取資料(原有系統這樣設計的)。

獲取到第三方資料後判斷資料庫中是否有該記錄(有唯一鍵),如有則執行更新操作,沒有則新增。並且如果第三方認為該資料已失效,需要從資料庫中刪除(邏輯刪除),並返回第三方刪除成功回撥,後續便不會再查到已失效的資料。

對應流程圖如下

在程式碼處理中對整個過程加事務@Transactional註解,即在對資料進行刪除(邏輯刪除,實際執行update語句)時會根據唯一索引對該行加鎖。在生成環境B專案頻繁出現Lock wait timeout exceeded; try restarting transaction 異常,經排查定位到該功能程式碼,排查程式碼發現該功能有如下程式碼

有經驗的同學應該已經看出問題所在,這裡將全域性異常捕獲,記錄錯誤日誌,但問題就出在catch這裡,由於異常被catch吞噬,@Transactional無法拿到異常,所以不會執行rollback回滾,導致一直佔用資料庫行鎖。(這裡的異常是呼叫第三方介面失敗,由於呼叫太頻繁,第三方介面崩潰,這裡後續也做了併發控制) 所以後續事務在執行更新該行記錄時由於得不到鎖而等待失敗,就報了Lock wait timeout exceeded; try restarting transaction 異常。

  

2.問題影響

由於該介面是在核心專案A中有客戶端發起呼叫,並在A專案中呼叫B專案,由於B專案死鎖無法返回結果,導致A專案前端大量請求阻塞,由於tomcat支援的請求執行緒數有限,該問題直接導致A服務宕機。影響較為嚴重。

 

3.如何解決

下面說下該問題解決思路,由於A專案宕機,在伺服器日誌中可以看到大量上述異常資訊,Lock wait說明出現了鎖問題。

  a.使用 SELECT * FROM INFORMATION_SCHEMA.INNODB_TRX;檢視當前事務,使用SELECT * FROM INFORMATION_SCHEMA.INNODB_LOCKS;檢視當前鎖定的事務,使用SELECT * FROM INFORMATION_SCHEMA.INNODB_LOCK_WAITS;檢視當前等鎖的事務。使用以上三個sql基本能定位到程式碼所在位置。

  b.定位到程式碼後,就要看具體的程式碼問題了,導致異常沒有回滾的原因很多。這裡說一個注意事項,使用@Transactional註解,預設只會對RuntimeException進行回滾,而對IOException和SQLException不會觸發回滾。如果要對兩個非執行時異常進行回滾,需要在@Transactional中加入@Transactional( rollbackForClassName = {"IOException","SQLException"})或對全域性異常Exception做回滾,配置為@Transactional(rollbackFor = Exception.class)。又或者捕獲IOException後手動丟擲一個RuntimeException

  

總結

  以上為博主在實際工作中遇到的問題,這裡記錄一下方便以後遇到類似問題可以快速定位並解決問題。也希望能對大家有幫助。

&n