PostgreSQL트랜잭션과 안전성

isolation level과 row locking

트랜잭션을 안전하게 묶는 것에서 한 걸음 더 나아가, 동시에 일어나는 작업 사이 간섭을 어떤 수준으로 막을지 정리합니다.

마지막 수정 2026년 3월 22일

기본 패턴

sql
BEGIN;

SELECT *
FROM jobs
WHERE status = 'queued'
FOR UPDATE SKIP LOCKED;

COMMIT;

설명

  • 트랜잭션이 "여러 작업을 한 덩어리로 묶는다"는 수준이라면, isolation level은 "동시에 실행되는 트랜잭션끼리 서로를 얼마나 보이게 할 것인가"를 정하는 규칙입니다.
  • PostgreSQL은 기본적으로 READ COMMITTED를 사용하며, 많은 업무에서는 충분합니다. 하지만 잔액 처리, 재고 경쟁, 작업 큐 같은 문제에서는 같은 행을 동시에 잡는 상황을 더 세밀하게 다뤄야 합니다.
  • 이때 row locking이 중요합니다. FOR UPDATE는 선택한 행을 다른 트랜잭션이 함부로 동시에 수정하지 못하게 잠그고, SKIP LOCKED는 이미 누군가 잡은 행을 건너뛰며 작업 큐 패턴을 만들 때 유용합니다.
  • 핵심 감각은 "트랜잭션이 있다고 자동으로 동시성 문제가 다 해결되진 않는다"는 점입니다. 어떤 행을 언제 읽고 잠그는지가 데이터 일관성의 실제 본체입니다.
  • isolation과 locking은 성능과 안전성 사이의 균형 문제입니다. 너무 강하게 잠그면 경합이 커지고, 너무 느슨하면 중복 처리나 경쟁 조건이 생길 수 있습니다.

빠른 정리

개념의미
READ COMMITTED기본 격리 수준
FOR UPDATE선택한 행 잠금
SKIP LOCKED잠긴 행은 건너뜀
잘 맞는 곳작업 큐, 재고 경쟁, 동시 수정 방지

주의할 점

"트랜잭션을 썼다"는 사실만으로 경쟁 조건이 사라지진 않습니다. 같은 데이터를 여러 작업이 동시에 집는 경로라면 읽기 시점, 잠금 범위, 재시도 정책까지 함께 설계하는 편이 중요합니다.

참고 링크

2 sources