當多事務以不同順序請求相同資源鎖,形成循環等待即發生死鎖。InnoDB 會自動檢測並回滾持有較少排他鎖的事務。本文以 StockPrice 表雙事務交叉更新為例,剖析成因並給出預防與處理方案。
-- 事務 1 START TRANSACTION; UPDATE StockPrice SET close = 45.50 WHERE stock_id = 4 AND date = '2002-05-01'; UPDATE StockPrice SET close = 19.80 WHERE stock_id = 3 AND date = '2002-05-02'; COMMIT; -- 事務 2 START TRANSACTION; UPDATE StockPrice SET high = 20.12 WHERE stock_id = 3 AND date = '2002-05-02'; UPDATE StockPrice SET close = 47.20 WHERE stock_id = 4 AND date = '2002-05-01'; COMMIT;
若事務 1 鎖住 (4, '2002-05-01'),事務 2 鎖住 (3, '2002-05-02'),雙方嘗試獲取對方持有的鎖 → 死鎖。
SHOW ENGINE INNODB STATUS 分析最新死鎖日誌,針對性調優索引與 SQL 順序。SELECT ... FOR UPDATE),嘗試 READ COMMITTED 減少間隙鎖。EXPLAIN 分析查詢計劃,減少掃描行數與鎖定範圍。FOR UPDATE/LOCK IN SHARE MODE,搭配 READ COMMITTED。SET AUTOCOMMIT=0; LOCK TABLES t1 WRITE, t2 READ; ... COMMIT; UNLOCK TABLES; 犧牲並發換無死鎖。建立單行 semaphore 表,事務進入前先 UPDATE semaphore SET val = val + 1,以行鎖實現邏輯序列化,InnoDB 死鎖檢測同樣有效。
避坑提醒:AUTOCOMMIT=1 時 LOCK TABLES 不會加 InnoDB 表鎖,務必關閉自動提交。