문제 상황
가정: 재고가 100개 있는데 재고를 1개씩 줄이는 요청이 100개가 동시에 들어옴.
기대 결과: 모든 요청이 처리되어 재고가 0이 되어야 하지만, race condition으로 인해 0이 되지 않을 수 있음.
Race Condition
- Race condition은 여러 스레드나 프로세스가 공유 자원에 동시에 접근하려고 할 때 발생하는 문제.
- 스레드 간 경합으로 인해 예상치 못한 결과가 발생할 수 있음.
- 예시: 동시에 재고 100개를 1씩 줄이는 요청이 들어오면 각각의 스레드가 100을 가져가서 재고 1개를 줄이는 작업을 수행하면 결과는 99가 됨.
해결 방법
- Lock 사용
- 공유 자원에 대한 접근을 하나의 스레드만 허용하는 방법.
- Java에서는
synchronized키워드를 사용하여 구현 가능. - 단일 서버에서는 동작할 수 있으나, 여러 서버에서는 여전히 문제가 발생할 수 있음.
- 성능 저하 가능성 있음.
- Pessimistic Lock
- 데이터에 실제로 Lock을 걸어서 동시 접근을 제어.
- 다른 트랜잭션이 해당 데이터에 접근하지 못하도록 함.
- 데드락 주의 필요.
- 병행성이 높을 때 성능이 개선될 수 있음.
- 단점 : 별도의 lock 을 잡기 때문에 성능 감소가 있을 수 있음.
- Optimistic Lock
- Lock을 사용하지 않고 버전 정보를 이용하여 데이터의 변경 여부를 확인.
- 업데이트 시 버전이 맞지 않으면 실패 후 재시도 필요.
- 업데이트 실패 시 재시도 로직을 개발자가 직접 작성해야함.
- 성능이 높을 수 있으며 충돌이 드물 때 적합.
- Named Lock
- 획득한 락의 이름을 사용하여 다른 세션과 구분됨.
- 획득한 락을 해제하지 않으면 다른 세션은 해당 락을 획득할 수 없음.
- 트랜잭션이 종료되더라도 자동으로 락이 해제되지 않음.
- 락을 획득한 후 일정 시간이 지나야 해제됨.
- Redis를 활용한 방법
- Redis를 활용하여 Lock을 구현할 수 있음.
- Redisson 라이브러리를 사용하면 편리하게 구현 가능.
- Pub-Sub 방식으로 Lock 해제 알림 가능.
- Lettuce를 이용한 Lock
- Lettuce는 Redis를 위한 자바 클라이언트 라이브러리.
- Spin Lock 방식으로 구현되어 동시에 많은 스레드가 Lock 획득 대기하면 Redis에 부하가 올 수 있음.
- Redisson을 이용한 Lock
- Redisson은 Redis 기반의 분산 객체와 서비스를 위한 라이브러리.
- Lock 획득 재시도 등을 내부에서 처리해줌.
- Pub-Sub 방식으로 Redis 부하가 적음.
- 별도의 라이브러리를 사용해야 하므로 사용법을 익히는데 시간이 소요됨.
선택과 고려 사항
- Lettuce나 Redisson을 사용하려면 Redis 인프라를 구축해야 함.
- MySQL은 이미 사용 중이라면 추가 비용 없이 활용 가능하며 어느 정도의 트래픽까지는 문제 없을 수 있음.
- 실무에서는 재시도가 필요한 경우 Redisson을 활용하고, 그 외에는 Lettuce 등을 고려할 수 있음.
'spring' 카테고리의 다른 글
| nestjs 맛보기 그리고 spring boot (0) | 2023.08.08 |
|---|---|
| github action + s3 + code deploy + docker + nginx 사용해서 배포하기 (0) | 2023.07.30 |
| 예외처리를 구조적이고 한 눈에 파악할 수 있게끔 커스텀 (0) | 2023.07.29 |
| Spring Boot gradle (0) | 2023.07.29 |