死鎖是事務型數據庫的固有特性。本文系統闡述死鎖成因、InnoDB 檢測機制、應用層處理模式、八大預防策略、表級鎖串行化方案、信號量表替代方案,構建完整死鎖應對知識體系。
經典場景:兩事務按不同順序更新同兩行數據
-- 事務 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 = 15.20 WHERE stock_id = 4 AND date = '2002-05-01';
COMMIT;
若執行交織:T1 鎖住 (4, '2002-05-01'),T2 鎖住 (3, '2002-05-02'),雙方各需對方持有的鎖 → 循環等待 → 死鎖。
1213 (ER_LOCK_DEADLOCK),SQLSTATE HY000SHOW ENGINE INNODB STATUS 中 LATEST DETECTED DEADLOCK 節點保留最近一次死鎖詳情⚠️ 對比:MyISAM/MEMORY 無死鎖檢測,依賴 lock_wait_timeout 超時回滾,效率極低。
@Retryable(maxAttempts = 3, backoff = @Backoff(delay = 100, multiplier = 2))
@Transactional
public void updateStockPrices(...) {
// 業務邏輯
}
死鎖非業務錯誤,**重試即可解決 99% 情況**。
寫入請求入隊列(Kafka/RocketMQ),單消費者順序處理,天然串行化避免死鎖。
READ COMMITTED (RC) 減少間隙鎖、Next-Key Lock,降低死鎖概率。需評估幻讀風險。EXPLAIN 驗證 type=ref/range、Using index,減少掃描行數、鎖定行數。DELETE/UPDATE 大量行 → 分批 LIMIT 1000 + SLEEP(0.1),減少鎖持有時間/範圍。SELECT ... FOR UPDATE / LOCK IN SHARE MODE 僅在業務強一致性要求時使用;常規查詢用 RC 級別一致性讀。SET AUTOCOMMIT = 0;
LOCK TABLES t1 WRITE, t2 READ;
-- 業務操作
COMMIT;
UNLOCK TABLES;
注意:AUTOCOMMIT=0 必須在 LOCK TABLES 前設置,否則隱式提交導致鎖失效。CREATE TABLE semaphore (id INT PRIMARY KEY, name VARCHAR(64)) ENGINE=InnoDB;
INSERT INTO semaphore VALUES (1, 'stock_update');
-- 事務開始前
UPDATE semaphore SET id = id + 1 WHERE name = 'stock_update';
-- 業務操作
COMMIT;
所有需串行化的事務先更新同一行,行鎖天然排隊。InnoDB 死鎖檢測對單行更新同樣有效。SHOW ENGINE INNODB STATUS\G
-- 關注 LATEST DETECTED DEADLOCK 節點:
-- 1. 兩事務的 SQL 文本
-- 2. 持有鎖 / 等待鎖 模式 (X/S/IX/IS/GAP)
-- 3. 索引名 / 空間坐標 (PAGE NO / HEAP NO)
-- 4. 事務隔離級別 / 啟動時間 / 修改行數
構建「死鎖日志 → 定位 SQL → 優化索引/調整順序/降級隔離 → 驗證無復發」閉環。
死鎖不可消除,但可**預防、檢測、快速恢復**。核心心法:
建立死鎖零容忍文化:每次死鎖必復盤、必優化、必驗證,將死鎖率壓至業務無感閾值。