기본 패턴
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