PostgreSQL 블로킹 락 확인 (누가 누굴 막는지)

브라우저 안에서만 처리됩니다. 입력한 내용과 선택한 파일은 서버로 전송되지 않습니다. 익명 방문 통계만 집계합니다.

지금 대기 중인 락 사이의 관계만 보여줍니다 — 트리에 없다고 락이 전혀 없다는 뜻은 아닙니다(권한 부족으로 안 보이거나, 붙여넣는 사이에 이미 풀렸을 수 있습니다). 판정은 검토 후보이지 결론이 아닙니다.

진단 SQL

SELECT
  blocked_locks.pid AS blocked_pid,
  blocked_activity.usename AS blocked_user,
  blocked_activity.query AS blocked_query,
  blocked_locks.mode AS blocked_mode,
  blocking_locks.pid AS blocking_pid,
  blocking_activity.usename AS blocking_user,
  blocking_activity.query AS blocking_query,
  blocking_locks.mode AS blocking_mode,
  round(extract(epoch FROM (now() - blocked_locks.waitstart))::numeric, 1) AS wait_seconds
FROM pg_catalog.pg_locks blocked_locks
JOIN pg_catalog.pg_stat_activity blocked_activity ON blocked_activity.pid = blocked_locks.pid
JOIN pg_catalog.pg_locks blocking_locks
  ON blocking_locks.locktype = blocked_locks.locktype
  AND blocking_locks.database IS NOT DISTINCT FROM blocked_locks.database
  AND blocking_locks.relation IS NOT DISTINCT FROM blocked_locks.relation
  AND blocking_locks.page IS NOT DISTINCT FROM blocked_locks.page
  AND blocking_locks.tuple IS NOT DISTINCT FROM blocked_locks.tuple
  AND blocking_locks.virtualxid IS NOT DISTINCT FROM blocked_locks.virtualxid
  AND blocking_locks.transactionid IS NOT DISTINCT FROM blocked_locks.transactionid
  AND blocking_locks.classid IS NOT DISTINCT FROM blocked_locks.classid
  AND blocking_locks.objid IS NOT DISTINCT FROM blocked_locks.objid
  AND blocking_locks.objsubid IS NOT DISTINCT FROM blocked_locks.objsubid
  AND blocking_locks.pid != blocked_locks.pid
JOIN pg_catalog.pg_stat_activity blocking_activity ON blocking_activity.pid = blocking_locks.pid
WHERE NOT blocked_locks.granted
ORDER BY wait_seconds DESC NULLS LAST;

복사한 SQL 을 psql --csv -f-(권장 — 파싱이 가장 견고합니다)로 실행하거나, psql 안에서 그대로 실행한 기본 출력도 지원합니다. 출력 전체를 아래에 붙여넣으세요.

필요 권한/확장 — 확장 불필요. pg_stat_activity 의 타 세션 query·usename 은 슈퍼유저 또는 pg_read_all_stats(pg_monitor 포함) 권한이 없으면 비어 보일 수 있음.

대기 중(granted=false)인 락만 나옵니다. 행 하나가 blocked_pid 하나가 blocking_pid 하나에게 막힌 간선 하나 — 체인은 blocking_pid 를 따라가며 재구성합니다.

사용법

  1. SQL 카드의 복사 버튼으로 진단 SQL(pg_locks × pg_stat_activity 조회)을 복사합니다.
  2. 복사한 SQL 을 psql 에서 그대로 실행합니다 — psql --csv -f- 를 권장하지만, 기본 실행 결과도 지원합니다.
  3. 출력 전체를 아래 textarea 에 붙여넣으면 누가 누굴 막고 있는지 블로킹 트리로 그려집니다.

자주 묻는 질문

붙여넣은 락 정보가 서버로 전송되나요?

아니요. 붙여넣은 내용은 이 브라우저 안에서만 파싱·분석되고 어디로도 전송되지 않습니다.

실행에 어떤 권한이 필요한가요?

확장은 필요 없습니다. 다만 자신이 실행하지 않은 다른 세션의 쿼리문·사용자명을 보려면 슈퍼유저 또는 pg_read_all_stats(pg_monitor 역할에 포함) 권한이 필요합니다 — 권한이 없으면 그 값들이 비어 보일 수 있습니다.

트리에 안 보이는 락도 있나요?

네. 이 도구는 지금 대기 중(granted=false)인 락 사이의 관계만 보여줍니다. 이미 잡고 있기만 하고 아무도 기다리지 않는 락, 붙여넣는 사이에 이미 풀린 락, 권한 부족으로 조회에 안 잡힌 락은 트리에 나타나지 않습니다 — "트리가 비었다"가 "락이 전혀 없다"의 증명은 아닙니다.

"⚠ 순환(교착 후보)" 는 무슨 뜻인가요?

어떤 pid 를 따라 막고 있는 체인을 계속 올라가면 다시 자기 자신으로 돌아온다는 뜻입니다(A가 B를 막고 B가 A를 막는 식). PostgreSQL 은 진짜 교착 상태(deadlock)를 자동으로 감지해 한쪽 트랜잭션을 강제로 중단시키므로 실제로 이 상태가 오래 유지되는 일은 드물지만, 붙여넣은 시점에 우연히 그 순간이 잡혔을 수 있습니다. 계속 반복해서 보인다면 애플리케이션의 락 획득 순서를 점검해 보세요.

블로킹을 해소하려면 어떻게 하나요?

가장 확실한 방법은 막고 있는 세션의 트랜잭션이 스스로 끝나기를(커밋 또는 롤백) 기다리는 것입니다. 급하게 끊어야 한다면 PostgreSQL 의 pg_terminate_backend(pid) 함수로 해당 세션의 연결을 강제 종료할 수 있지만, 이는 그 세션이 하던 작업을 전부 롤백시키는 되돌릴 수 없는 조치입니다 — 어떤 pid 를 끊을지는 이 도구가 대신 정해주지 않으니, 트리에서 해당 세션이 정말 무엇을 하고 있는지(위 쿼리 텍스트) 직접 확인한 뒤 신중히 실행하세요.