萬盛學電腦網

您现在的位置: 萬盛學電腦網 >> 程式設計 >  >> 資料庫 >> MySQL 教學

MySQL 死鎖防範:主從表插入刪除並發衝突的三種解法與進階策略

死鎖成因還原

-- 事務 A (插入)
START TRANSACTION;
INSERT INTO parent (id, ...) VALUES (1, ...); -- 鎖 parent 行 (X)
INSERT INTO child (parent_id, ...) VALUES (1, ...); -- 需鎖 child 行 (X)

-- 事務 B (刪除)
START TRANSACTION;
DELETE FROM child WHERE parent_id = 1; -- 鎖 child 行 (X)
DELETE FROM parent WHERE id = 1; -- 需鎖 parent 行 (X)

-- 死鎖:A 持有 parent 等 child,B 持有 child 等 parent

三大經典解法對比

方案原理優點缺點/風險適用性
1. 移除外鍵應用層維護完整性,刪除順序統一為 Parent→Child徹底消除鎖順序衝突失去資料庫層面約束,需應用嚴格保證高併發、應用層成熟
2. 刪除前轉移外鍵先 UPDATE child SET parent_id = 0 (虛擬根節點),再刪 parent,最後刪 child保留外鍵約束,統一訪問順序額外 UPDATE 開銷、需維護虛擬行、業務邏輯複雜強一致性要求高
3. ON DELETE CASCADE刪 Parent 級聯刪 Child,刪除事務只操作 Parent簡單、原子性、無應用邏輯大量級聯刪除鎖擴大、觸發器連鎖、誤刪風險層級明確、資料量可控

進階防範策略 (不改業務邏輯)

1. 統一訪問順序 (最通用)

應用層規範:所有事務按固定順序訪問表 (如 Parent → Child)。插入自然符合,刪除改為先標記軟刪除 (UPDATE status),異步真刪。

2. 行鎖排序

-- 事務內按主鍵排序處理
SELECT * FROM parent WHERE id IN (1,2,3) ORDER BY id FOR UPDATE;
SELECT * FROM child WHERE parent_id IN (1,2,3) ORDER BY parent_id, id FOR UPDATE;

3. 縮短事務、減少鎖持有

  • 事務前完成非 DB 操作 (HTTP 呼叫、複雜計算)
  • 批次操作分批提交
  • 避免事務中 SELECT 無索引掃描 (間隙鎖擴大)

4. 降低隔離級別 (謹慎)

SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;
-- 需配合 binlog_format=ROW
-- 會出現不可重複讀、幻讀,業務必須容忍

5. 死鎖檢測與自動重試

-- 應用層重試模板 (指數退避)
for attempt in 1..3:
  try:
    tx.begin()
    # 業務邏輯
    tx.commit()
    break
  except DeadlockError:
    if attempt == 3: raise
    sleep(2^attempt * 0.05 + random())
    tx.rollback()

監控與事後分析

-- 即時查看死鎖
SHOW ENGINE INNODB STATUS\G
-- 關注 LATEST DETECTED DEADLOCK

-- 歷史死鎖 (需開啟)
innodb_print_all_deadlocks = ON
-- 錯誤日誌記錄完整死鎖圖

總結建議

  1. 首選 統一訪問順序 + 應用層重試,零改架構、風險最低
  2. 外鍵級聯刪除僅用於資料量小、層級固定、無觸發器連鎖場景
  3. 高併發核心業務:軟刪除 + 異步清理 徹底避開刪除鎖衝突
  4. 監控 Innodb_deadlocks 計數器,設定告警
copyright © 萬盛學電腦網 all rights reserved