목차
1. 낙관적 락
2. 비관적 락(명시적 락)
3. 원자적 UPDATE
1. 낙관적 락
1) 개념
- 데이터에 version 컬럼을 두고 UPDATE 하기 조회 당시 version과 실제 update 시 version이 같아야 업데이트 성공
- 경쟁을 막지 않고 허용하되 커밋 시점에 충돌을 감지하여 실패 처리한다.
- ⇒ A,B,C가 거의 동시에 접근한다면 A만 성공 B,C는 바로 예외 터짐
- 동시 접근 자체가 예외를 발생시키기 때문에 그에 대한 재시도or실패 처리가 꼭 필요하다.
2) 낙관적 락이 적합한 상황
충돌이 드물고 만약 발생하더라도 실패 후 다시 시도하는 비용이 크지 않은 경우에 적합함.
예시 상황
- 게시물의 제목이나 내용 등을 수정하는 경우
- 관리자 화면에서 간헐적으로 수정되는 값
이런 상황에서는 여러 사용자가 동시에 같은 row를 수정할 가능성이 낮고 재시도 비용이 낮다
⇒ 결론적으로 대부분의 요청이 충돌 없이 성공하고 드물게 충돌하면 실패/재시도가 자연스러운 경우에 적합
3) 낙관적 락이 부적합한 상황
1. 정원/재고/좌석 같은 카운터 증가 구조
예를 들어 정원 50명인 스터디에 40명이 동시에 가입을 시도한다고 가정한다.
낙관적 락을 사용하면 다음과 같은 일이 벌어짐.
- 40명이 동시에 같은 version을 읽는다.
- 1명만 UPDATE 성공한다.
- 나머지 39명은 전부 실패한다.
이때 후처리 방식에 따라 각각 문제가 발생한다.
case 1. 재시도 처리
- 실패한 39명의 재시도 처리 자체가 다시 충돌을 일으킬 수 있음. 최대 39번 반복이 아닌 그 이상으로 반복하여 트래픽의 폭발 가능
case 2. 바로 종료 처리
- 아직 자리가 남아 있어도 충돌로 실패하는 사용자가 발생.
- 사용자 입장에선 가입 신청이 실패한 이유가 모호하다.(서버에서 다른 누군가 동시에 신청했다고 예외가 터졌으니)
- 성공 여부가 타이밍에 따라 운으로 결정된다.
2. 이벤트성 처리 로직
- 한정 쿠폰 100장
- 콘서트 좌석
- 한정 특가 상품
이런 경우 충돌이 예외적으로 드문 상황이 아니라 충돌이 보편적인 상황이 된다.
위와 같은 이유로 이런 경우도 낙관적락을 사용하면 안 된다.
2. 비관적 락(명시적 락)
1) 개념
- 경쟁이 발생할 수 있는 row를 select for update 등으로 먼저 잠그고 그 잠금이 해제될 때까지 다른 트랜잭션의 접근을 대기시키는 방식이다.
- 충돌 자체를 못 일으키게 줄을 세우는 방식
- 낙관적락은 5명이 동시에 접근하면 4명은 실패하지만 해당 처리는 5명 모두 성공 가능
2) 비관적 락이 적합한 상황
자원에 대한 경쟁이 잦고 여러 Row를 동시에 검증 & 수정해야 하는 경우
예시 상황
- 계좌 이체: A 계좌 차감, B계좌 증감
- 여러 상품을 동시에 검증 후 차감해야 하는 주문
=> 위의 케이스와 같이 여러 row에 대한 검증 및 수정 시점에 정합성이 보장받아야 할 경우에 적합하며
단일 row에 대한 검증과 수정이 가능하다면 대부분 원자적 update가 유리함
3) 비관적 락 사용 시 주의점
- 락 구간을 최대한 짧게 잡는다.
- 잠그고 기다리는 비용이 있으니 락 잡고 외부 호출(메일/결제 API) 같은 오래 걸리는 작업이 끼면 병목 현상이 발생한다.
(select for update로 잡금 조회를 호출하는 메서드에서 외부 API 등을 호출하지 않는다)
- 잠그고 기다리는 비용이 있으니 락 잡고 외부 호출(메일/결제 API) 같은 오래 걸리는 작업이 끼면 병목 현상이 발생한다.
- 타임아웃 정책을 명확히 설정해야 한다
- 비관적 락은 대기를 하는 동작이기 때문에 TIME OUT 설정 및 처리가 필수
- DEAD LOCK에 대한 예외 처리/재시도 전략이 있어야 한다.
- DB 격리 수준에 따라 잠금 범위가 달라질 수 있다.
- READ COMMITTED ⇒ 해당 row만 잠금
- REPEATABLE READ ⇒ 경우에 따라 범위로 잡혀 여러 row 가 잠길 수 있음
3. 원자적 UPDATE
1) 개념
- UPDATE 문 자체에 조건을 포함시켜 조건이 만족하는 경우에만 변경이 일어나도록 하는 방식
- 읽기-검증-쓰기 과정을 분리하지 않고 쓰기 시점에 한 번에 검증 + 수정을 수행한다
- 충돌을 대기로 흡수하지 않고 조건 불일치 시 0 row update로 즉시 실패시킨다
- 조건 만족 -> update 결과 1 -> 성공
- 조건 불만족 -> update 결과 0 -> 실패
코드 예시
UPDATE study_group
SET current_member_count= current_member_count+1 -- 조건 만족하면 현재원 증가
WHERE id= ?
AND current_member_count< capacity; -- 현재원이 정원보다 적어?
이와 같이 검증과 수정 자체를 하나의 쿼리에서 처리하여 읽기와 쓰기 사이에 간극이 있을 수 없다.
2) 원자적 UPDATE가 적합한 상황
경쟁은 정상이며 일부만 성공하면 되는 구조에 적합
- 한정된 자원을 두고 일부만 성공시키고 나머진 실패 처리하는 경우
- 실패가 비즈니스적으로 자연스러운 경우
예시 상황
- 정원 제한 스터디 가입
- 한정 쿠폰 100장 발급
- 이벤트 경품 50개
- 재고 감소 처리
3) 원자적 update가 부적합한 상황
복합 검증이 필요한 경우
- 여러 row를 읽어 계산 후 판단해야 하는 경우
- 집계/상태 조합/정책 판단이 복잡한 경우
- 단일 UPDATE 조건으로 표현이 어려운 경우
⇒ 이런 복합 검증이 필요한 상황에선 명시적 락으로 처리
'동시성 제어' 카테고리의 다른 글
| Spring 동시성 테스트 코드 재현(명시적 락,CountDownLatch) (0) | 2026.01.23 |
|---|