DBAtlas · データベース運用アトラス

デッドロック分析

症状

'deadlock detected'、'ORA-00060'、'chosen as deadlock victim' — トランザクションが互いをロックし、1 つが中断される。

1. デッドロックは正常だが、頻発すべきではない

DB はデッドロックを検出し、犠牲者を選んで解決する — これは設計どおりの動作である。目標は頻度を減らすこと。アプリケーションは犠牲になったトランザクションを再試行すべきである。まずデッドロックグラフを取得する。

2. デッドロックグラフ/詳細を取得する

どの 2 つ (以上) のセッションが、どのリソースとどのロックモードで競合したか。

Oracle

-- Check the trace file referenced in the alert log (deadlock graph). Also:
SELECT * FROM dba_hist_active_sess_history WHERE event LIKE 'enq: TX%' ORDER BY sample_time DESC;

PostgreSQL

-- log_lock_waits=on; deadlock details are in postgresql.log. Check the deadlock_timeout setting.

MySQL

SHOW ENGINE INNODB STATUS\G  -- LATEST DETECTED DEADLOCK section

SQL Server

-- Deadlock XML in the system_health Extended Events session (ring_buffer, recent events):
SELECT x.ev.value('@timestamp','datetime2') AS event_time, x.ev.query('.') AS deadlock_xml
FROM (SELECT CAST(t.target_data AS XML) td FROM sys.dm_xe_session_targets t
      JOIN sys.dm_xe_sessions s ON s.address = t.event_session_address
      WHERE s.name = N'system_health' AND t.target_name = N'ring_buffer') r
CROSS APPLY r.td.nodes('//RingBufferTarget/event[@name="xml_deadlock_report"]') AS x(ev);
-- For longer history, the event_file target:
-- SELECT CAST(event_data AS XML) FROM sys.fn_xe_file_target_read_file(N'system_health*.xel', NULL, NULL, NULL) WHERE object_name = N'xml_deadlock_report';

3. 競合パターンを解消する

最も一般的な原因: 2 つのトランザクションがリソースに逆の順序でアクセスしていること。すべてのコードパスで同じ順序でリソースにアクセスする。2 番目に多い原因: インデックスの欠如による広範な範囲ロック — 必要なインデックスを追加する。

4. ロックの期間と範囲を縮小する

トランザクションを短く保つ (トランザクション内にユーザー操作を含めない)。適切な分離レベルを選ぶ。必要な場合は行ロックを早期に取得する (決定的な順序での SELECT ... FOR UPDATE)。

5. アプリケーションでの再試行は必須

デッドロックを完全になくすことはできない。犠牲になったトランザクションに自動再試行 (指数バックオフ) を実装する。これによりユーザーにエラーを見せずに済む。

DBAtlas で開く →