낙관적 락과 비관적 락
충돌 빈도와 재시도 비용에 따라 Version 검증과 선점 Lock을 선택한다.
이 문서의 목차
Overview
낙관적 락은 실제 DB Lock을 오래 잡지 않고 저장 시 Version이 그대로인지 검사한다. 비관적 락은 충돌할 것으로 보고 먼저 DB Lock을 얻어 경쟁 요청을 기다리게 한다.
핵심 용어
| 용어 | 의미 |
|---|---|
| Version Column | 읽을 때와 쓸 때 동일한지 비교하는 충돌 표식 |
| Lost Update | 두 변경 중 하나가 다른 변경을 덮는 현상 |
| PESSIMISTIC_WRITE | JPA에서 변경 의도의 DB Locking Read |
| Retry | 충돌 후 최신 상태를 다시 읽어 업무를 재실행 |
| Atomic Update | 읽기 없이 조건을 포함해 한 SQL로 변경 |
왜 필요한가
동시에 같은 Coupon 수량이나 문서를 수정하면 마지막 Write가 앞선 변경을 덮을 수 있다. 무조건 직렬화하면 안전하지만 대기가 늘고, 무조건 낙관적으로 하면 충돌 시 값비싼 작업을 반복한다.
핵심 원리와 내부 동작
낙관 방식은 UPDATE ... WHERE id=? AND version=?의 영향 Row가 0이면 충돌이다. JPA @Version이 이 패턴을 지원한다. 비관 방식은 SELECT ... FOR UPDATE가 Commit까지 경쟁 변경을 대기시킨다.
Example
@Entity
class Product {
@Id Long id;
@Version Long version;
int stock;
}낙관 충돌은 최신 Entity를 새 Transaction에서 다시 읽어 제한된 횟수로 Retry한다. Email 발송 같은 Side Effect까지 전체 재실행하지 않도록 경계를 분리한다. 재고 단순 감소는 조건부 Atomic Update가 더 낫다.
실무에서 발생하는 문제와 Trade-off
Hot Row에서 낙관 Retry가 폭증하면 DB 작업량이 늘어난다. 비관 Lock은 Queue처럼 대기시켜 Timeout과 Deadlock이 생긴다. 긴 사용자 Think Time 동안 DB Transaction을 열어두지 않는다. Version은 다른 경로의 Bulk Update도 일관되게 증가시켜야 한다.
흔한 오해
- JPA
@Version은 DB Row Lock을 미리 잡는 기능이 아니다. - 낙관적 락도 충돌 처리를 생략하면 오류일 뿐 해결책이 아니다.
- 비관적 락이 중복 API 요청이나 메시지 재전달까지 자동 해결하지 않는다.
- Retry는 무제한이 아니라 Backoff·Jitter·멱등성을 필요로 한다.
Production Considerations
Optimistic Conflict Rate, Retry 성공률, Lock Wait, Timeout, Hot Aggregate를 측정한다. 충돌률과 업무 재실행 비용을 기준으로 선택하고 Unique Constraint와 Atomic Update를 먼저 검토한다. 장애 시 사용자에게 재시도 가능한 충돌인지 명확히 전달한다.
다른 사람에게 설명한다면
30초: “낙관적 락은 Version으로 저장 시 충돌을 감지해 충돌이 드문 경우 대기를 줄입니다. 비관적 락은 먼저 DB Lock을 잡아 충돌이 잦은 짧은 작업을 순서화합니다. 단순 불변식은 Atomic Update가 더 좋을 수 있습니다.”
2분: 두 요청이 version=10을 읽는 예와 FOR UPDATE 대기 흐름을 비교하고, Retry Side Effect·Hot Row·Timeout을 포함해 선택 기준을 말한다.
Interview Questions
@VersionUpdate SQL은 어떤 형태인가?- 충돌 Retry에서 Side Effect가 위험한 이유는?
- Hot Row에 낙관적 락을 쓰면?
- Atomic Update가 더 나은 예는?
Related Topics
DB Lock 지도, Locking Read와 연결한다.
SOURCE REFERENCES
이 문서의 근거
본문은 Dev Atlas 안에서 완결되며, 검증이 필요할 때만 원문을 확인할 수 있습니다.