業務新上了一個功能,在發佈的過程當中,系統報出了數據庫死鎖異常:mysql
com.mysql.jdbc.exceptions.jdbc4.MySQLTransactionRollbackException: Deadlock found when trying to get lock; try restarting transaction
死鎖發生在一個事務中,事務對多個表進行了操做。在報錯日誌中,死鎖發生在tableA與tableB。一開始懷疑這次發佈的某個改動中對上面這兩張表新增了select或update操做。將注意力用在排查這個問題上。排查後發現沒有相關的變動,又猜想是不是因爲更改形成併發請求進來,接口原來是有加分佈式鎖的,需求更改中縮小了分佈式鎖的粒度,確實是有可能形成併發請求。但很快又否認了,秒殺場景下的併發量尚且不會發生死鎖,況且是這個接口?以爲問題應該別有所在。先回滾了需求後,聯繫dba執行了命令SHOW ENGINE INNODB STATUS
將死鎖日誌拉取了出來:sql
從死鎖日誌能夠看到事務(1)TRANSACTION
嘗試更新表A,等待表A的鎖(1)WATING FOR THIS LOCK TO BE GRANTED
.但此時另一個事務(2)TRANSACTION
已經持有了表A的鎖:(2)HOLDS THE LOCK(S)
,同時也在等待表B的鎖(2)WATING FOR THIS LOCK TO BE GRANTED
. 狀況可能以下所示:數據庫
事務(1) | 事務(2) |
---|---|
持有表1的寫鎖,並更新了表1 | |
等待表1的寫鎖 | |
等待表2的寫鎖 |
因爲事務2一直都獲取不到表2的寫鎖,事務2沒法提交,所以事務2持有的表1鎖沒有釋放(在事務執行過程當中,若是有加鎖操做,這個鎖須要等事務提交時釋放),致使事務1一直在等待表1的寫鎖,從而最終致使死鎖。那麼表2的寫鎖被哪一個事務持有了?有沒有多是事務1?也便是下面這種狀況:併發
事務(1) | 事務(2) |
---|---|
持有表2的寫鎖,並更新了表2 | |
持有表1的寫鎖,並更新了表1 | |
等待表1的寫鎖 | |
等待表2的寫鎖 |
因爲更新的是同一個用戶的同一行記錄,這種狀況可能在sql執行順序不一致所致使,因此對比了需求變動先後的事務邏輯,果真發現了端倪:分佈式
變動前:rest
事務開始 「 更新表1 更新表2 」 事務提交
變動後:日誌
事務開始 「 更新表2 更新表1 」 事務提交
在發佈的過程當中,有部分機器的代碼處於變動前,有部分機器的代碼處於變動後,最終致使了上述的死鎖問題。此時緣由已經明瞭,可是疑惑的是爲何死鎖出現時立刻就報錯,不會進行等待?查閱文檔發現:死鎖檢測開了後,發生死鎖會立馬回滾。死鎖檢測關閉,鎖等待超時時間這個設置纔會生效:code