需了解死鎖先看這一篇
死鎖是如何被發(fā)現(xiàn)和解決的?這篇文章告訴你
一、死鎖的產(chǎn)生原因
- 死鎖發(fā)生在兩個(gè)或多個(gè)事務(wù)相互等待對(duì)方釋放鎖,導(dǎo)致它們都無(wú)法繼續(xù)執(zhí)行的情況,形成死鎖。
- 這種情況在并發(fā)高的系統(tǒng)中比較常見(jiàn),尤其是在多個(gè)事務(wù)同時(shí)操作相同的數(shù)據(jù)時(shí)。
常見(jiàn)場(chǎng)景包括:
- 不同順序訪問(wèn)資源:事務(wù)A先操作表1再操作表2,事務(wù)B先操作表2再操作表1。
- 索引缺失:全表掃描導(dǎo)致鎖范圍擴(kuò)大,增加沖突概率。
- 長(zhǎng)事務(wù):事務(wù)長(zhǎng)時(shí)間未提交,導(dǎo)致鎖持有時(shí)間過(guò)長(zhǎng)。
二、死鎖場(chǎng)景復(fù)現(xiàn)(以MySQL為例)
- 復(fù)現(xiàn)死鎖可以幫助理解問(wèn)題發(fā)生的條件,從而更好地預(yù)防和解決。
1. 準(zhǔn)備測(cè)試表和數(shù)據(jù)
CREATE TABLE account (
id INT PRIMARY KEY,
balance DECIMAL(10,2)
);
INSERT INTO account VALUES (1, 1000.00), (2, 2000.00);
2. 模擬兩個(gè)事務(wù)交叉更新
事務(wù)A:先更新id=1,再更新id=2。
BEGIN;
UPDATE account SET balance = balance - 100 WHERE id = 1;
UPDATE account SET balance = balance + 100 WHERE id = 2;
COMMIT;
事務(wù)B:先更新id=2,再更新id=1。
BEGIN;
UPDATE account SET balance = balance - 200 WHERE id = 2;
UPDATE account SET balance = balance + 200 WHERE id = 1;
COMMIT;
3. 觀察死鎖
- 按順序執(zhí)行事務(wù)A和事務(wù)B的
UPDATE
語(yǔ)句。 - 事務(wù)A嘗試更新id=2時(shí),因事務(wù)B持有鎖而等待。
- 事務(wù)B嘗試更新id=1時(shí),因事務(wù)A持有鎖而等待。
- 數(shù)據(jù)庫(kù)檢測(cè)到死鎖,自動(dòng)回滾其中一個(gè)事務(wù):
1205 - Lock wait timeout exceeded; try restarting transaction

三、解決死鎖的核心方法
- 常見(jiàn)的解決策略包括設(shè)置合理的事務(wù)隔離級(jí)別、優(yōu)化事務(wù)邏輯、使用超時(shí)機(jī)制、以及數(shù)據(jù)庫(kù)自動(dòng)檢測(cè)和處理死鎖。
1. 數(shù)據(jù)庫(kù)自動(dòng)處理
2. 代碼層優(yōu)化
- 統(tǒng)一資源訪問(wèn)順序:所有事務(wù)按相同順序操作表或記錄。
UPDATE account SET ... WHERE id = 1;
UPDATE account SET ... WHERE id = 2;
- 減少事務(wù)粒度:避免長(zhǎng)事務(wù),盡快提交或回滾。
@Transactional(timeout = 5)
public void transfer() { ... }
- 使用樂(lè)觀鎖:通過(guò)版本號(hào)避免行鎖競(jìng)爭(zhēng)。
UPDATE account
SET balance = 900, version = version + 1
WHERE id = 1 AND version = 1;
3. 數(shù)據(jù)庫(kù)配置調(diào)優(yōu)
4. 重試機(jī)制
四、死鎖分析工具
MySQL死鎖日志:
- 查看
SHOW ENGINE INNODB STATUS
輸出,分析TRANSACTION
和WAITING FOR THIS LOCK
部分。
pt-deadlock-logger(Percona工具):
實(shí)時(shí)監(jiān)控死鎖事件并記錄。
pt-deadlock-logger --user=root --password=123456 --run-time=10
性能模式(Performance Schema):
MySQL 5.6+開(kāi)啟性能模式,監(jiān)控鎖等待事件。
SELECT * FROM performance_schema.events_transactions_current;

五、預(yù)防死鎖
- 事務(wù)設(shè)計(jì)原則:
- 短事務(wù)優(yōu)先,及時(shí)提交。
- 避免交叉更新多張表。
- 統(tǒng)一操作順序:
所有業(yè)務(wù)邏輯按固定順序訪問(wèn)資源(如按主鍵升序操作)。 - 監(jiān)控與報(bào)警:
配置Prometheus監(jiān)控死鎖次數(shù),超過(guò)閾值觸發(fā)告警。 - 壓力測(cè)試:
使用JMeter模擬并發(fā)事務(wù),驗(yàn)證死鎖概率。
六、總結(jié)
- 復(fù)現(xiàn)死鎖:通過(guò)交叉更新不同順序的資源,觀察數(shù)據(jù)庫(kù)自動(dòng)回滾。
- 解決方案:
- 統(tǒng)一資源訪問(wèn)順序,減少鎖競(jìng)爭(zhēng)。
- 優(yōu)化索引和事務(wù)設(shè)計(jì),降低死鎖概率。
- 結(jié)合重試機(jī)制和數(shù)據(jù)庫(kù)自動(dòng)處理,提升系統(tǒng)容錯(cuò)性。
- 關(guān)鍵工具:數(shù)據(jù)庫(kù)日志、性能分析工具、壓力測(cè)試框架。
轉(zhuǎn)自https://juejin.cn/post/7481837718718644274
該文章在 2025/3/17 10:33:31 編輯過(guò)