핵심 정리
SELECT pid, locktype, relation::regclass, mode, granted
FROM pg_locks
WHERE NOT granted;PostgreSQL 잠금 문제는 "느린 쿼리"처럼 보일 수 있지만, 실제로는 다른 트랜잭션이 잡고 있는 lock을 기다리는 상태일 수 있습니다.
읽는 법
lock wait는 대기이고 deadlock은 순환 대기다
lock wait는 한 트랜잭션이 다른 트랜잭션의 잠금 해제를 기다리는 상태입니다. 기다리던 트랜잭션이 커밋하거나 롤백하면 진행할 수 있습니다. deadlock은 두 개 이상의 트랜잭션이 서로 상대가 가진 잠금을 기다리는 순환 구조입니다. PostgreSQL은 deadlock을 감지하면 한 트랜잭션을 강제로 중단해 순환을 끊습니다.
-- 세션 A
BEGIN;
UPDATE accounts SET balance = balance - 100 WHERE id = 1;
UPDATE accounts SET balance = balance + 100 WHERE id = 2;
-- 세션 B가 반대 순서로 같은 행을 수정하면 deadlock 위험
BEGIN;
UPDATE accounts SET balance = balance - 50 WHERE id = 2;
UPDATE accounts SET balance = balance + 50 WHERE id = 1;같은 테이블을 쓰더라도 항상 같은 순서로 행을 잠그면 deadlock 가능성이 크게 줄어듭니다. 계좌 이체라면 작은 ID부터 잠그는 식으로 순서를 고정합니다.
행 잠금은 수정 대상 행에 걸린다
UPDATE, DELETE, SELECT ... FOR UPDATE는 대상 행에 row-level lock을 겁니다. 같은 행을 동시에 수정하려는 트랜잭션은 앞 트랜잭션이 끝날 때까지 대기합니다. 행 잠금은 테이블 전체를 막는 잠금과 다르지만, 조건이 넓거나 인덱스가 없어 많은 행을 건드리면 대기 범위가 커집니다.
BEGIN;
SELECT *
FROM jobs
WHERE status = 'queued'
ORDER BY created_at
LIMIT 1
FOR UPDATE;작업 큐처럼 여러 worker가 동시에 같은 후보를 잡는 구조에서는 FOR UPDATE SKIP LOCKED가 더 적합할 수 있습니다. 단, 건너뛴 행은 나중에 다시 처리될 수 있도록 상태 전이를 명확히 해야 합니다.
pg_locks와 pg_stat_activity를 같이 봐야 한다
pg_locks는 어떤 lock이 잡혔고 대기 중인지 보여 주지만, 쿼리 텍스트와 실행 상태는 pg_stat_activity에서 봅니다. 잠금 문제를 볼 때는 대기자와 보유자를 같이 찾아야 원인을 알 수 있습니다.
SELECT
a.pid,
a.state,
a.wait_event_type,
a.wait_event,
a.query
FROM pg_stat_activity AS a
WHERE a.wait_event_type = 'Lock';운영 환경에서는 오래 열린 트랜잭션도 함께 확인해야 합니다. 아무 작업을 하지 않는 idle in transaction 세션이 잠금을 오래 잡고 있으면 뒤 쿼리들이 줄줄이 대기할 수 있습니다.
예방 기준
| 상황 | 먼저 볼 지점 |
|---|---|
| 같은 행을 동시에 수정 | row lock 대기 |
| 여러 행을 서로 다른 순서로 수정 | deadlock 위험 |
| 작업 큐 소비 | FOR UPDATE SKIP LOCKED |
| 장시간 대기 | pg_stat_activity.wait_event_type |
| 원인 세션 확인 | pg_locks + pg_stat_activity |
주의할 점
deadlock은 "쿼리가 느린 문제"가 아니라 트랜잭션들이 서로 상대의 잠금을 기다리는 순환 문제입니다. 인덱스를 추가하는 것만으로 해결되지 않을 수 있습니다. 같은 업무 흐름에서는 행을 잠그는 순서를 고정하고, 트랜잭션을 짧게 유지하는 설계가 우선입니다.
잠금 대기 쿼리를 강제로 종료하기 전에 어떤 트랜잭션이 잠금을 보유하고 있는지 확인해야 합니다. 대기자를 죽이면 증상만 사라질 수 있고, 보유자를 죽이면 롤백 비용이 크게 발생할 수 있습니다.
참고 링크
2 sources