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

블로킹 및 락 위기

증상

쿼리가 멈추고 'lock wait timeout'이 발생하며, 많은 세션이 대기 중이지만 CPU는 낮다.

1. 블로킹 체인의 시작점(root blocker)을 찾는다

누가 누구를 막고 있는지 파악한다. 진짜 원인은 자신은 아무것도 기다리지 않으면서 다른 세션을 기다리게 하는 세션이다. 체인 맨 위의 세션에 집중한다.

Oracle

SELECT blocking_session, sid, serial#, username, seconds_in_wait, sql_id FROM v$session WHERE blocking_session IS NOT NULL ORDER BY seconds_in_wait DESC;

PostgreSQL

SELECT pid, usename, pg_blocking_pids(pid) blocked_by, state, query FROM pg_stat_activity WHERE cardinality(pg_blocking_pids(pid))>0;

MySQL

SELECT waiting_pid, waiting_query, blocking_pid, blocking_query FROM sys.innodb_lock_waits;

SQL Server

SELECT session_id, blocking_session_id, wait_type, wait_resource FROM sys.dm_exec_requests WHERE blocking_session_id<>0;

MongoDB

db.currentOp({ waitingForLock: true })

2. root blocker가 무엇을 하는지 파악한다

열려 있지만 커밋되지 않은 트랜잭션인가? 애플리케이션이 'idle in transaction' 상태로 두었는가? 긴 배치인가? 무작정 종료하기 전에 무엇인지 파악한다. 곧 끝날 수도 있다.

Oracle

SELECT s.sid, s.prev_sql_id, t.start_time, t.used_ublk FROM v$transaction t JOIN v$session s ON t.ses_addr=s.saddr;

PostgreSQL

SELECT pid, state, xact_start, now()-xact_start age, query FROM pg_stat_activity WHERE pid=<root_pid>;

3. 결정: 대기, 애플리케이션에서 해결, 또는 종료

곧 끝나면 기다린다. 애플리케이션이 커밋/롤백할 수 있으면 그렇게 하도록 한다. 그렇지 않고 업무가 멈췄다면 root blocker를 종료한다. 종료 전에 영향(롤백 시간, 데이터)을 고려한다.

4. 필요하면 root blocker를 종료한다

체인 맨 앞의 세션만 종료한다. 그 아래 세션들은 자동으로 해제된다.

Oracle

ALTER SYSTEM KILL SESSION 'sid,serial#' IMMEDIATE;

PostgreSQL

SELECT pg_terminate_backend(<root_pid>);

MySQL

KILL <root_id>;

SQL Server

KILL <root_session_id>;

MongoDB

db.killOp(<opid>)

5. 근본 원인을 해결한다

누락된 인덱스가 넓은 락을 유발할 수 있고, 애플리케이션이 트랜잭션을 너무 오래 열어 둘 수 있으며, 격리 수준이 잘못되었을 수 있다. 인덱스를 추가하고, 트랜잭션을 짧게 하고, 'idle in transaction' timeout을 설정하며, 적절한 격리 수준을 선택한다.

DBAtlas에서 열기 →