select for update와 serializable 격리수준
1. 직렬화 가능 고립(Serializable Isolation)의 개념
- 정의: 여러 트랜잭션이 동시에 실행되더라도, 마치 한 번에 하나씩 순차적으로(직렬로) 실행된 것과 같은 결과를 보장하는 가장 높은 단계의 고립 레벨입니다.
- 목표: 데이터의 일관성을 완벽하게 유지하는 것입니다.
2. 구현 방식 비교: 비관적 vs 낙관적
① 비관적 동시성 제어 (Pessimistic Concurrency Control)
- 작동 방식:
SELECT FOR UPDATE 등을 사용하여 데이터에 독점 잠금(Exclusive Lock)을 겁니다.
- 10개의 트랜잭션이 오면, 1개가 잠금을 획득하고 나머지 9개는 대기(Blocking)합니다.
- 첫 번째가 끝나면 두 번째가 잠금을 얻는 식으로 하나씩 처리합니다.
- 장점: 트랜잭션이 실패할 확률이 거의 없습니다. 구현이 단순하고 로직이 명확합니다.
- 단점: 동시성과 처리량(Throughput)이 현저히 떨어집니다. 모든 작업이 줄을 서야 하므로 속도가 느려집니다.
② 낙관적 동시성 제어 (Optimistic Concurrency Control)
- 작동 방식: "서로 방해하지 않을 것"이라고 가정하고 잠금 없이 일단 실행합니다.
- 커밋 시점에 데이터베이스가 다른 트랜잭션에 의해 데이터가 변경되었는지(충돌 여부)를 감지합니다.
- 충돌이 발견되면 해당 트랜잭션을 실패시키고 오류를 반환합니다.
- 장점: 여러 트랜잭션이 동시에 실행될 수 있어 병렬성과 속도가 유지됩니다.
- 단점: 충돌 발생 시 트랜잭션이 실패하므로, 애플리케이션 레벨에서 '재시도(Retry) 로직'을 구현해야 하는 번거로움이 있습니다.
3. 요약 및 선택 기준 (Trade-off)
| 구분 |
비관적 제어 (SELECT FOR UPDATE) |
낙관적 제어 (Serializability 체크) |
| 핵심 철학 |
"충돌할 것이 뻔하니 미리 잠그자" |
"충돌 안 할 것 같으니 일단 가보자" |
| 동시성 |
낮음 (차단 발생) |
높음 (병렬 실행) |
| 실패 가능성 |
낮음 (기다리면 성공함) |
있음 (충돌 시 실패 및 재시도 필요) |
| 복잡성 |
단순함 |
복잡함 (재시도 로직 필요) |
결론: 무엇을 선택해야 하는가?
- 비관적 제어 권장: 동시 요청이 많지 않거나, 데이터 무결성이 절대적으로 중요하며 재시도 로직을 만들기 싫은 경우.
- 낙관적 제어 권장: 동시 요청이 매우 많고 빠른 응답 속도가 중요하며, 충돌이 빈번하게 발생하지 않을 것이라고 예상되는 경우.