一、常見死鎖場景
1.?不同順序的鎖獲取
-
場景:事務A按順序更新?
行1 → 行2
,事務B按?行2 → 行1
?順序更新。 -
原因:雙方各持有一把鎖,同時請求對方持有的鎖,形成循環等待。
2.?索引缺失導致鎖升級
-
場景:更新操作未命中索引,導致全表掃描,鎖住所有記錄(甚至間隙鎖)。
-
原因:無索引時,行鎖可能升級為表鎖,增大死鎖概率。
3.?間隙鎖(Gap Lock)沖突
-
場景:在可重復讀(RR)隔離級別下,事務A刪除某范圍數據,事務B嘗試插入相同范圍的數據。
-
原因:間隙鎖阻塞插入操作,導致互相等待。
4.?唯一鍵沖突
-
場景:高并發下插入相同唯一鍵數據,事務A和事務B同時嘗試插入,觸發唯一約束沖突。
-
原因:回滾時釋放鎖的順序可能引發死鎖。
5.?混合操作(SELECT ... FOR UPDATE + UPDATE)
-
場景:事務A先?
SELECT ... FOR UPDATE
?鎖定行1,再更新行2;事務B反向操作。 -
原因:顯式鎖與隱式鎖交叉請求。
二、解決方案
1.?統一鎖順序
-
操作:確保所有事務按相同順序訪問資源(如統一按主鍵升序更新)。
-
示例:事務A和事務B均按?
行1 → 行2
?順序更新。
2.?優化索引設計
-
操作:為高頻查詢/更新字段添加索引,避免全表掃描。
-
示例:對?
WHERE
?或?UPDATE
?條件字段建立索引,縮小鎖范圍。
3.?降低事務粒度
-
操作:減少事務內操作,盡快提交釋放鎖。
-
示例:避免在事務中執行耗時操作(如外部API調用)。
4.?設置合理隔離級別
-
操作:使用?
READ COMMITTED
?替代?REPEATABLE READ
,減少間隙鎖的使用。 -
注意:需權衡一致性與并發性能。
5.?重試機制
-
操作:捕獲死鎖異常(錯誤碼?
1213
),等待隨機時間后重試事務。 -
代碼示例(偽代碼):
python
復制
try:execute_transaction() except DeadlockError:wait(random_time)retry()
6.?避免長事務
-
操作:監控并拆分執行時間過長的事務,減少鎖持有時間。
-
工具:通過?
SHOW PROCESSLIST
?或?INFORMATION_SCHEMA.INNODB_TRX
?監控事務。
7.?使用樂觀鎖
-
操作:通過版本號或時間戳實現無鎖更新。
-
示例:
UPDATE table SET col = new_val, version = version + 1 WHERE id = 1 AND version = old_version;
8.?分析死鎖日志
-
操作:通過?
SHOW ENGINE INNODB STATUS
?查看死鎖日志,定位沖突SQL。 -
關鍵信息:關注?
LATEST DETECTED DEADLOCK
?部分,分析鎖類型(行鎖、間隙鎖)和事務操作路徑。
三、案例分析
案例1:不同順序更新導致的死鎖
-
事務A:
BEGIN; UPDATE users SET score = 100 WHERE id = 1; UPDATE users SET score = 200 WHERE id = 2; COMMIT;
-
事務B:
BEGIN; UPDATE users SET score = 300 WHERE id = 2; UPDATE users SET score = 400 WHERE id = 1; COMMIT;
-
解決:統一按主鍵升序更新(先更新?
id=1
?再?id=2
)。
案例2:唯一鍵插入死鎖
-
場景:兩個事務同時插入相同唯一鍵數據。
-
解決:使用?
INSERT ... ON DUPLICATE KEY UPDATE
?或先檢查后插入(需捕獲異常)。
四、總結
場景 | 解決策略 |
---|---|
不同順序鎖競爭 | 統一資源訪問順序 |
無索引導致鎖升級 | 優化索引設計 |
間隙鎖沖突 | 降低隔離級別或避免范圍操作 |
長事務阻塞 | 拆分事務,快速提交 |
高并發寫入沖突 | 樂觀鎖或重試機制 |
通過合理設計事務、優化索引、分析日志及結合重試機制,可有效減少死鎖發生概率。
更加詳細案例參考:
Mysql死鎖場景案例及解決方案-CSDN博客