MySQL已產(chǎn)生死鎖的解決方法及永久避免方案
引言
死鎖是多個(gè)事務(wù)互相持有對(duì)方需要的鎖,且都不釋放,導(dǎo)致互相無(wú)限等待的現(xiàn)象,MySQL 會(huì)自動(dòng)檢測(cè)并終止其中一個(gè)事務(wù)(拋出死鎖異常),讓另一個(gè)正常執(zhí)行。
你現(xiàn)在的核心需求分兩步:
- 緊急處理:已經(jīng)發(fā)生死鎖,怎么快速恢復(fù)業(yè)務(wù)?
- 根治問(wèn)題:怎么讓死鎖不再頻繁出現(xiàn)?
一、已經(jīng)產(chǎn)生死鎖:3步快速解決
1. 無(wú)需手動(dòng)殺進(jìn)程!MySQL 會(huì)自動(dòng)處理
MySQL 內(nèi)置死鎖檢測(cè)(默認(rèn)開(kāi)啟),一旦檢測(cè)到死鎖:
- 立即回滾代價(jià)最小的那個(gè)事務(wù)
- 給應(yīng)用拋出
Deadlock found when trying to get lock; try restarting transaction異常 - 另一個(gè)事務(wù)會(huì)自動(dòng)繼續(xù)執(zhí)行,死鎖瞬間解除
結(jié)論:已發(fā)生的死鎖不需要人工干預(yù),MySQL 自己會(huì)秒級(jí)解決。
2. 查看死鎖日志:找到根源(最重要)
死鎖已經(jīng)發(fā)生,必須查日志定位原因,執(zhí)行命令:
-- 查看最近一次死鎖的詳細(xì)信息 SHOW ENGINE INNODB STATUS;
在結(jié)果中找到 LATEST DETECTED DEADLOCK 段落,里面會(huì)告訴你:
- 哪兩個(gè)事務(wù)死鎖了
- 各自執(zhí)行的 SQL
- 各自持有什么鎖、等待什么鎖
- 死鎖發(fā)生的時(shí)間
這是解決死鎖的唯一依據(jù)。
3. 臨時(shí)應(yīng)急:卡住的事務(wù)不自動(dòng)解除?
極少數(shù)情況(關(guān)閉了死鎖檢測(cè)),事務(wù)會(huì)一直卡住,執(zhí)行以下命令手動(dòng)處理:
-- 1. 查看正在運(yùn)行的事務(wù),找到卡住的 ID SELECT * FROM INFORMATION_SCHEMA.INNODB_TRX; -- 2. 殺掉卡住的事務(wù) KILL 事務(wù)ID;
二、永久避免死鎖:4個(gè)核心方案(必做)
死鎖無(wú)法100%杜絕,但99%的死鎖都能通過(guò)規(guī)范避免。
1. 事務(wù)保持短小,不要長(zhǎng)事務(wù)
- 死鎖概率和事務(wù)執(zhí)行時(shí)間成正比
- 禁止在事務(wù)里嵌套:網(wǎng)絡(luò)請(qǐng)求、文件IO、人工操作、sleep
- 原則:快進(jìn)快出,執(zhí)行完立即提交/回滾
壞例子
BEGIN; UPDATE 表 SET ...; -- 這里調(diào)用了外部接口,耗時(shí)3秒,鎖一直持有,極易死鎖 COMMIT;
2. 所有表固定訪(fǎng)問(wèn)順序(最有效)
死鎖的本質(zhì):訪(fǎng)問(wèn)順序相反
- 事務(wù)A:先鎖訂單 → 再鎖庫(kù)存
- 事務(wù)B:先鎖庫(kù)存 → 再鎖訂單
→ 瞬間死鎖
解決方案:
所有業(yè)務(wù)代碼,必須按相同順序操作表/行
例如:統(tǒng)一先操作庫(kù)存,再操作訂單。
3. 給查詢(xún)條件加索引
無(wú)索引會(huì)導(dǎo)致行鎖升級(jí)為表鎖,死鎖概率暴增。
-- 錯(cuò)誤:無(wú)索引,會(huì)鎖全表 UPDATE user SET money=100 WHERE name='張三'; -- 正確:name 有索引,只鎖單行 UPDATE user SET money=100 WHERE name='張三';
死鎖日志里如果看到 LOCK_MODE: X, REC_NOT_GAP: nil 基本就是無(wú)索引導(dǎo)致。
4. 業(yè)務(wù)代碼捕獲死鎖異常,自動(dòng)重試
死鎖是偶發(fā)異常,重試就能成功,這是兜底方案。
Java 示例:
try {
// 執(zhí)行數(shù)據(jù)庫(kù)操作
} catch (SQLTransactionRollbackException e) {
if (e.getMessage().contains("Deadlock")) {
// 死鎖異常,重試1-2次
retry();
}
}三、快速排查死鎖 Checklist
- 執(zhí)行
SHOW ENGINE INNODB STATUS;看死鎖詳情 - 檢查兩個(gè)事務(wù)是否訪(fǎng)問(wèn)表/行順序相反
- 檢查更新語(yǔ)句是否沒(méi)有索引
- 檢查是否有長(zhǎng)事務(wù)
- 業(yè)務(wù)代碼是否沒(méi)有重試機(jī)制
總結(jié)
- 已產(chǎn)生的死鎖:MySQL 自動(dòng)回滾一個(gè)事務(wù),無(wú)需手動(dòng)處理
- 查死鎖原因:用
SHOW ENGINE INNODB STATUS; - 根治死鎖:固定訪(fǎng)問(wèn)順序 + 加索引 + 短事務(wù) + 代碼重試
- 兜底:業(yè)務(wù)捕獲死鎖異常自動(dòng)重試,用戶(hù)無(wú)感知
到此這篇關(guān)于MySQL已產(chǎn)生死鎖的解決方法及永久避免方案的文章就介紹到這了,更多相關(guān)MySQL已產(chǎn)生死鎖解決與避免內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Linux下指定mysql數(shù)據(jù)庫(kù)數(shù)據(jù)配置主主同步的實(shí)例
Linux下指定數(shù)據(jù)庫(kù)數(shù)據(jù)配置主主同步的實(shí)例,有需要的朋友可以參考下2013-01-01
MySql用DATE_FORMAT截取DateTime字段的日期值
MySql截取DateTime字段的日期值可以使用DATE_FORMAT來(lái)格式化,使用方法如下2014-08-08
MySQL中有哪些情況下數(shù)據(jù)庫(kù)索引會(huì)失效詳析
這篇文章主要給大家介紹了關(guān)于MySQL中有哪些情況下數(shù)據(jù)庫(kù)索引會(huì)失效的相關(guān)資料,文中通過(guò)圖文介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用mysql具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2018-07-07
使用MySQL Workbench構(gòu)建ER圖的詳細(xì)教程
ER圖又稱(chēng)實(shí)體-聯(lián)系圖(Entity Relationship Diagram),提供了表示實(shí)體類(lèi)型、屬性和聯(lián)系的方法,用來(lái)描述現(xiàn)實(shí)世界的概念模型,MySQL?Workbench是一個(gè)強(qiáng)大的數(shù)據(jù)庫(kù)設(shè)計(jì)工具,提供了便捷的數(shù)據(jù)導(dǎo)入導(dǎo)出功能,本文介紹了使用MySQL Workbench構(gòu)建ER圖的詳細(xì)教程2024-06-06
淺談MySql 視圖、觸發(fā)器以及存儲(chǔ)過(guò)程
這篇文章主要介紹了MySql 視圖、觸發(fā)器以及存儲(chǔ)過(guò)程的的相關(guān)資料,文中講解非常細(xì)致,代碼幫助大家更好的理解和學(xué)習(xí),感興趣的朋友可以了解下2020-06-06
MySQL可以使用斜線(xiàn)來(lái)當(dāng)字段的名字
無(wú)意中發(fā)現(xiàn)MySQL可以使用斜線(xiàn)來(lái)當(dāng)字段的名字,下面有個(gè)示例,需要的朋友可以參考下2014-03-03

