工作中遇到的數據庫死鎖問題 - 排查方案 CannotAcquireLockException

問題·重現

正常情況下,銷售人員在使用我們的銷售系統爲客戶創建訂單時候,後臺java代碼就會開啓事務,然後往數據庫中添加訂單信息和訂單詳情信息,以及一些其他業務操作。但由於某次異常操作(可能是網絡或者其他的原因),導出系統出現問題無法工作。

於是進行代碼調試,發現在向數據庫中插入數據的時候,一直卡在添加數據這個方法上,等待了很長一段時間,一直無法響應。在經過一段時間後,通過try…catch…捕獲了 org.springframework.dao.CannotAcquireLockException異常,如圖。

後臺代碼展示,就是在orderGoodsService.addOrderGoods這方法一直卡住。
在這裏插入圖片描述
後臺打印處理的錯誤日誌如下:

### Error updating database.  Cause: com.mysql.cj.jdbc.exceptions.MySQLTransactionRollbackException: Lock wait timeout exceeded; try restarting transaction
### The error may exist in file [D:\Idea workspace\bussiness\target\classes\mapper\order\OrderGoodsMapper.xml]
### The error may involve com.dwnest.business.dao.order.OrderGoodsMapper.insert-Inline
### The error occurred while setting parameters
### SQL: insert into order_goods (id, goods_id, consumer_id,       coupon_id, discount_scale,       product_name, sku_id, specification, sku_pic_url, item_number,       main_material, auxiliary_material, out_length,        out_wide, out_high, cycle_date1,        cycle_date2, nums, sale_price,        discount_price, total_price, status,        `explain`, store_id, create_time,       create_by, update_time, update_by,change_subtract       )     values (?, ?, ?,       ?, ?,       ?, ?, ?,       ?, ?, ?, ?,       ?,       ?, ?, ?,        ?, ?, ?,        ?, ?, ?,        ?, ?, ?,        ?, ?, ?,?       )
### Cause: com.mysql.cj.jdbc.exceptions.MySQLTransactionRollbackException: Lock wait timeout exceeded; try restarting transaction
; Lock wait timeout exceeded; try restarting transaction; nested exception is com.mysql.cj.jdbc.exceptions.MySQLTransactionRollbackException: Lock wait timeout exceeded; try restarting transaction

然後後臺報出異常,前臺提示訂單創建失敗:
在這裏插入圖片描述

問題·分析

雖然後臺在try…catch的時候報出了org.springframework.dao.CannotAcquireLockException異常,但是在打印出日誌的時候,顯示的是com.mysql.cj.jdbc.exceptions.MySQLTransactionRollbackException: Lock wait timeout exceeded; try restarting transaction。提示的是Lock wait timeout exceeded(鎖等待超時);try restarting transaction (嘗試開啓新事務);

就是說,在我們開啓事務插入數據的時候一直未獲取到鎖,等待超時了。所有現在的問題就是查看爲什麼無法獲取到鎖。

問題·排除

這裏可以確定問題是出現於數據庫中,要從數據庫中進行排查。

由於當前請求獲取不到鎖,可以能有以下情況:

  1. 數據庫是否發生表鎖,整張表被鎖定,也就無法往裏插入數據了。通過以下命令查詢是否表鎖,然後得到的結果爲空,表示沒有發生表鎖,這個情況排除;
show open table where in_use > 0; //查詢是否表鎖
  1. 數據庫中該表是否有事務搶佔了該鎖,導致該請求無法獲取鎖,然後一直等待,等待時間超過數據庫設置的默認時間就會失敗。通過以下命令查詢是否有事務被鎖住,果然,查到了有一條記錄,從2020-05-09 15:03開始的,狀態時(RUNNING)運行,線程id爲428847。
select * from information_schema.innodb_trx // 查詢運行的事務,是否被鎖住

在這裏插入圖片描述
雖然查詢出一條記錄,但是無法確定是否時該條數據導致的,於是再次發生插入訂單數據請求,在orderGoodsService.addOrderGoods這個方法卡住的時候,查看數據庫的select * from information_schema.innodb_trx這條命令,得到兩條結果。第一條結果是剛添加的,狀態時Lock WAIT鎖等待,接下來的情況就是一直無法獲取鎖,最後會執行失敗。原因就是第二條結果是導致的。所以可以確定就是線程id爲428847,這個結果導致的。

在這裏插入圖片描述
好傢伙,從下午3點,一直卡到5點,還在運行,一直持有鎖不放,導致當前插入數據失敗。

問題·解決

由於之前的某種原因,導致事務一直卡住,所以只需要把之前的事務殺死就可以了,

kill id; // id爲線程id,使用該命令就可以殺死某條線程

然後查看數據庫的select * from information_schema.innodb_trx就會發現結果爲空,在調用插入訂單的時候,就成功了。這個問題就解決了。

其他命令

select * from information_schema.innodb_locks // 查看當前鎖定的事務
select * from information_schema.innodb_locks_waits //查看當前等待鎖的事務

記錄一下開發中存在的問題。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章