DBAtlas · 데이터베이스 운영 아틀라스

데드락 분석

증상

'deadlock detected', 'ORA-00060', 'chosen as deadlock victim' — 트랜잭션이 서로를 잠그고 하나가 중단된다.

1. 데드락은 정상이지만 빈번해서는 안 된다

DB는 데드락을 감지하고 victim을 선택해 해결한다 — 설계된 동작이다. 목표는 빈도를 줄이는 것이다. 애플리케이션은 victim 트랜잭션을 재시도해야 한다. 먼저 deadlock graph를 확보한다.

2. deadlock graph/상세 정보를 확보한다

어떤 두(또는 그 이상의) 세션이 어떤 리소스와 어떤 락 모드로 충돌했는가?

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. 충돌 패턴을 해소한다

가장 흔한 원인: 두 트랜잭션이 리소스에 반대 순서로 접근하는 것. 모든 코드 경로에서 같은 순서로 리소스에 접근한다. 두 번째로 흔한 원인: 누락된 인덱스로 인한 넓은 범위 락 — 필요한 인덱스를 추가한다.

4. 락 지속 시간과 범위를 줄인다

트랜잭션을 짧게 유지한다(트랜잭션 안에 사용자 상호작용을 포함하지 않는다). 적절한 격리 수준을 선택한다. 필요하면 행 락을 일찍 획득한다(결정적 순서의 SELECT ... FOR UPDATE).

5. 애플리케이션 재시도는 필수다

데드락을 완전히 없앨 수는 없다. victim 트랜잭션에 대해 자동 재시도(exponential backoff)를 구현한다. 이렇게 하면 사용자가 오류를 보지 않는다.

DBAtlas에서 열기 →