PostgreSQL 락 유형과 pg_locks — 잠금 계층 이해

PostgreSQL의 테이블 수준 락 8가지, 행 수준 락 4가지, Advisory 락의 특성과 충돌 행렬, pg_locks 뷰와 pg_stat_activity를 조합해 락 대기 체인을 진단하는 방법, lock_timeout과 statement_timeout 설정으로 장기 대기를 방지하는 전략을 정리합니다.

· 4 min read · PALDYN Team

지난 글에서 SSI가 직렬화 이상을 탐지하는 원리를 살펴봤다. 이번에는 PostgreSQL의 잠금 체계 전반을 다룬다. MVCC 덕분에 읽기와 쓰기는 대부분 충돌하지 않지만, DDL 변경이나 명시적 잠금이 필요한 시나리오에서는 락 충돌이 운영의 뇌관이 된다.

락 유형 개요

PostgreSQL의 잠금은 크게 세 계층으로 나뉜다.

  1. 테이블 수준 락 — LWLock(Lightweight Lock)을 사용하며 SQL 문마다 자동 획득
  2. 행 수준 락 — 힙 튜플 헤더에 기록, MVCC와 연동
  3. Advisory 락 — 애플리케이션이 임의 키로 잠금 설정
-- 현재 세션이 보유한 테이블 락 확인
SELECT l.relation::regclass AS table_name,
       l.mode,
       l.granted,
       a.pid,
       a.query
FROM   pg_locks        l
JOIN   pg_stat_activity a ON a.pid = l.pid
WHERE  l.locktype = 'relation'
  AND  a.pid <> pg_backend_pid();

테이블·행·Advisory 락 계층 구조

테이블 수준 락 충돌 행렬

AccessExclusiveLock은 모든 락과 충돌한다. DROP TABLE, TRUNCATE, VACUUM FULL, LOCK TABLE 명령이 이 모드를 획득한다. 운영 중인 테이블에 ALTER TABLE을 실행하면 대기 중인 SELECT 하나만 있어도 뒤따라 들어오는 모든 쿼리가 줄을 서게 된다.

-- ALTER TABLE 전 준비
SET lock_timeout = '3s';          -- 3초 내 획득 못 하면 오류
SET statement_timeout = '30s';    -- 문장 전체 30초 제한

-- 락 대기열 확인 후 작업
SELECT count(*) FROM pg_stat_activity
WHERE  wait_event_type = 'Lock';

-- 필요 시 블로커 강제 종료
SELECT pg_terminate_backend(pid)
FROM   pg_stat_activity
WHERE  state = 'idle in transaction'
  AND  now() - xact_start > interval '10 minutes';

행 수준 락과 FOR UPDATE

행 수준 락은 SELECT FOR UPDATE 등으로 명시 획득하거나 UPDATE·DELETE가 자동으로 걸어준다. 힙 튜플 헤더의 infomask 필드에 기록되며, 잠긴 행을 다른 트랜잭션이 수정하려 하면 대기하거나 충돌 오류를 반환한다.

-- 행 수준 락 모드 (강도 오름차순)
-- FOR KEY SHARE     : FK 참조 무결성 체크용
-- FOR SHARE         : 공유 읽기 락
-- FOR NO KEY UPDATE : KEY 컬럼 제외 업데이트
-- FOR UPDATE        : 가장 배타적, DELETE와 동일

-- 행 락 확인
SELECT pid, locktype, mode, granted, relation::regclass
FROM   pg_locks
WHERE  locktype = 'tuple';

Advisory 락

Advisory 락은 데이터베이스 행이나 페이지와 무관하게 64비트 정수 키로 잠금을 설정한다. 분산 환경에서 외부 lock manager 없이 상호 배제가 필요할 때 유용하다.

-- 세션 단위 Advisory 락 (세션 종료 시 자동 해제)
SELECT pg_advisory_lock(42);
-- 비차단 버전 (즉시 반환: 획득 성공 여부 bool)
SELECT pg_try_advisory_lock(42);

-- 트랜잭션 단위 Advisory 락 (COMMIT/ROLLBACK 시 해제)
SELECT pg_advisory_xact_lock(42);

-- 해제
SELECT pg_advisory_unlock(42);

락 대기 체인 진단

pg_locks 기반 대기 체인 진단 쿼리

-- 데드락 로그 확인 (postgresql.conf)
-- deadlock_timeout = 1s  (기본값: 1초 후 탐지 시작)
-- log_lock_waits = on

-- 락 대기 그래프 (blocker/blocked PID 쌍)
SELECT blocking_pids, pid, query
FROM   pg_stat_activity
WHERE  cardinality(pg_blocking_pids(pid)) > 0;

pg_blocking_pids(pid) 함수는 특정 PID를 차단 중인 PID 배열을 반환한다 (PostgreSQL 9.6+). 빠르게 blocker를 찾는 가장 간단한 방법이다.

운영 팁

문제진단해결
ALTER TABLE 대기pg_locks + lock_timeout트래픽 최소 시간대 실행
장기 idle-in-transactionpg_stat_activityidle_in_transaction_session_timeout 설정
데드락 빈발pg_log deadlock 항목락 획득 순서 통일

지난 글: SSI — 직렬화 스냅샷 격리의 충돌 감지

다음 글: SELECT FOR UPDATE와 SKIP LOCKED — 행 수준 잠금 패턴


읽어주셔서 감사합니다. 😊