1. 동시성 제어가 필요한 이유
- 문제 상황
- 열차 예매 서비스에서는 여러 사용자가 동시에 같은 좌석을 예약하려는 상황이 발생할 수 있음
- 예: 명절에 KTX 1호차 3A에 좌석에 동시에 3명이 예약을 요청한 경우
- 이 때 동시성이 제어되지 않으면
- 중복 예약되어 한 좌석에 여러 명이 예약됨
- 데이터의 정합성이 깨짐
- 해결 방법
- 먼저 예약한 사람만 성공하고, 나머지는 실패하도록 락(Lock)을 걸어야 함
- 락을 구현하는 방식은 DB 수준의 네임드 락(Named Lock), Zookeeper와 Redis 기반의 분산락이 있음
2. Lock을 구현하기 위해 Redis를 선택한 이유
- In-memory 기반의 빠른 처리 속도
- 동시성 이슈를 해결하기 위한 방법으로는 MySQL의 네임드 락, Zookeeper도 존재하지만,
이들은 디스크 기반 DB이기 때문에 응답 속도 면에서 한계가 있음 - Redis는 메모리 기반 데이터 저장소로, 디스크 기반의 DB보다 데이터 접근 속도 및 응답 시간이 빠름
- 예매 시에는 락을 획득하고, 중복인지 검사하고, 예약을 저장하는 흐름이 수 ms내에 빠르게 처리되어야 하므로, 이러한 실시간성 요구에 Redis가 가장 적합한 성능을 제공한다고 판단함
- 동시성 이슈를 해결하기 위한 방법으로는 MySQL의 네임드 락, Zookeeper도 존재하지만,
- 안정적인 동시성 제어를 위한 중항 집중형 락 관리
- Redis는 싱글 스레드 기반으로 명령을 처리하는 구조로, 동시에 여러 요청이 들어와도 내부적으로 명령어가 순차 처리됨.
- 동시에 접근하더라도 단 하나의 요청만 락을 획득하고, 나머지는 대기하거나 실패 처리하는 방식.
- 또한 Redis는 네트워크로 접근 가능한 중앙 저장소 역할을 하므로, 여러 서버 인스턴스가 스레드에 동시에 접근하더라도, 공유 자원(좌석, 대기열)에 대한 락을 중앙에서 안전하게 제어할 수 있음. 해당 프로젝트는 단일 서버 환경에서 동작하지만, 멀티 스레드 환경에서의 안정적인 락 제어를 위하여 도입하였음
- TTL 설정을 통한 자동 만료 및 이벤트 처리 지원
- 사용자가 예약 도중 결제를 진행하지 않거나 응답이 없는 경우, 해당 예약 정보를 일정 시간이 지나면 자동으로 삭제해야 함
- 이 때, Redis는 TTL(Time To Live)를 설정할 수 있어서, 예약의 유효 시간을 직접 제어할 수 있음
- TTL 만료 시에는 Redis의 Keyspace Event 기능을 활용하여 대기열 자동 제거 및 순번 갱신 로직과 연결할 수 있음
- Spring Boot 환경에서의 호환성
- Lettuce, Redisson 과 같은 클라이언트를 지원하기 때문에 분산락 구현이 간편함
3. Redis 분산락 구현 방식 : Redisson 선택
프로젝트에서는 Redis 기반 분산락을 구현하기 위한 클라이언트로 Lettuce와 Redisson 두 가지 방법을 비교하였음.
- Lettuce
- Spring Data Redis의 기본 클라이언트
- 저수준의 명령어(SETNX)를 사용하여 락을 수동 구현
- 장점 : 의존성 추가 없이 간단하게 락 구현
- 단점
- 락 획득, 해제, 재시도 등의 로직을 직접 구현해야 함
- 예외나 장애 발생시 데드락 가능성
- 락 소유자 확인할 수 없어서, 락 해제 누락시 위험
- Spin Lock 방식이기 때문에, 다수의 스레드가 락을 요청할 경우, Redis에 부하 발생
- Redisson
- Redis를 기반으로 고수준의 분산 객체와 락 기능을 제공하는 외부 라이브러리
- 단점 : 외부 라이브러리 의존 (redisson dependency)
- 장점
- tryLock(waitTime, leaseTime)과 같은 API로 락 획득과 자동 해제를 직관적으로 처리
- isHeldByCurrentThread() 메서드를 통해 락 소유자 확인 가능
- TTL과 락 자동 해제 기능 있어서 데드락 방지
- 내부적으로 pub-sub 구조를 사용하여 락 해제를 기다리는 스레드에게 알리므로, 불필요한 polling 부하가 없음
- Spring Boot와 통합이 쉬워서 유지보수 쉬움
- Redisson을 선택한 이유
: 이 프로젝트는 좌석 예약, 대기열 진입, TTL 기반 예약 만료 처리 등 여러 사용자가 동시에 하나의 자원에 접근하는 상황이 발생할 수 있는 구조를 가지고 있음. 이러한 환경에서는 락 획득 실패시 자동 재시도, 일정 시간 이후에 자동 해제, 락 소유자 확인 등 락을 제어하는 기능이 필수적이며, 이를 위해서 Lettuce보다 Redisson이 더욱 적합하다고 판단하였음.
Redisson은 락 처리 로직이 직관적이며 안정성이 높고, 개발자가 직접 예외 처리나 재시도 로직을 구현하지 않아도 되는 구조를 제공하며, 복잡한 동시성 제어 로직도 단순하고 안전하게 구현할 수 있다는 점에서 선택하였음
4. Redisson 분산락 적용 사례
- 좌석 중복 예약 방지
- 대기열 진입 충돌 방지
- 예약 TTL 기반 자동 만료 처리
LIST
'👩🏻💻Project' 카테고리의 다른 글
| ✅ 통합 테스트에서 외부 API를 호출할지, 아니면 Mocking 할지 (0) | 2025.07.05 |
|---|---|
| 🤔 외부 API 어떤 방식으로 호출할지, RestTemplate vs WebClient (2) | 2025.07.04 |
| ✅Repository의 테스트 환경에 대한 단위테스트 전략 (1) | 2025.07.03 |
| 🚀[트러블슈팅] Toss API 오류 대응을 위한 커스텀 ErrorHandler 적용 및 재시도 제어 개선 (2) | 2025.07.02 |
| 🔐Spring Security가 적용된 Controller 단위 테스트에서 403 에러 해결 (1) | 2025.07.01 |