요약
이 글은 튜닝 레포트 시스템에서 사용자의 동시 리액션 요청이 발생할 때 Redis 캐시의 JSON 객체가 충돌하며 생기는 Race Condition 문제를 어떻게 해결했는지, 실전 적용 경험을 바탕으로 정리한 기술 포스트다.
다음과 같은 내용을 다룬다.
문제 정의: Redis에 저장된 JSON을 동시에 수정할 때 발생하는 동시성 충돌
- 5가지 대안 기술 비교: HINCRBY, Lua Script, Redis WATCH, SessionCallback, Redisson 분산 락
- 선택 기준과 검토 포인트: 구조 호환성, 유지보수 난이도, 멀티 인스턴스 적합성 등
- 최종 선택 – Redisson RLock: 기존 JSON 구조를 유지하며, Spring Boot 환경에서 안전하고 직관적으로 구현 가능
문제 정의
멀티 인스턴스(WAS) 환경에서 사용자가 동시에 게시글에 리액션을 보낼 경우,
Redis에 캐싱된 ReportItem JSON 객체를 "읽기 → 수정 → 다시 쓰기"하는 과정에서
Race Condition(동시성 충돌)이 발생할 수 있음.
예시 문제
- 두 사용자가 동시에 HEART(반응)를 누르면,
- 동일한 JSON을 읽고 각각 +1 후 저장 → 최종 반영은 한 번뿐 → 리액션 수가 실제보다 적게 저장됨
고려한 대안들
1. Redis Hash + HINCRBY
- 구조: 리액션 수치를 Redis Hash에 따로 저장 → HINCRBY reactions:{reportId} HEART 1
- 장점: Redis 원자 연산 → 락 없이 동시성 보장
- 단점: 기존 ReportItem 구조와 분리 → 리팩토링 비용 큼
2. Redis Lua Script
- 구조: JSON 수정 포함 모든 작업을 Lua 스크립트로 작성해 Redis에서 원자적 실행
- 장점: 완전한 atomic 처리, JSON 구조 유지 가능
- 단점: Lua 디버깅/관리 복잡, 유지보수 어려움
3. Redis CAS 패턴 (WATCH / MULTI / EXEC)
- 구조: WATCH로 key 감시, 다른 클라이언트가 key 변경 시 EXEC 실패 → 재시도
- 장점: Redis native 방식으로 낙관적 락 구현
- 단점: 재시도 로직 필요, 실패 가능성 존재, 구현 난이도 높음
4. SessionCallback + MULTI/EXEC
- 구조: Spring Data Redis에서 여러 Redis 연산을 하나의 트랜잭션 블록으로 묶음
- 장점: 간단한 Redis 연산 원자 처리 가능
- 단점: 분산 환경에서의 충돌 방지 불가능, WATCH 없음 → 완전한 충돌 방지 어려움
5. Redisson 분산 락 (최종 선택) ⭐️⭐️⭐️
- 구조: RLock lock = redissonClient.getLock("lock:report:{id}") → 락 획득 후 JSON 수정 → 저장 → 락 해제
- 장점
- JSON 캐시 구조 유지 가능
- 멀티 인스턴스 간 동시성 완벽 보장
- 락 TTL/재시도 구성 가능
- 구현이 직관적이고 재사용 용이
- 단점
- 약간의 성능 오버헤드 (락 획득/해제)
- 락 TTL 설정 실수 시 데드락 위험
한 눈에 기술 비교
| 전략 특징 | 특징 | 원자성 | 분산 환경 적합성 | 유지보수 |
| HINCRBY | 수치 필드 분리 | ✅ | ✅ | ❌ (구조 분리) |
| Lua Script | Redis 내부 처리 | ✅ | ✅ | ❌ (복잡) |
| CAS (WATCH) | 낙관적 락 | ⭕ | ❌ | ⚠️ (재시도 필요) |
| SessionCallback | Redis 트랜잭션 | ✅ | ❌ | ✅ |
| Redisson RLock | 비관적 락 | ✅ | ✅ | ✅ |
최종 선택: Redisson 분산 락
| 이유 | 상세 |
| 구조 호환성 | ReportItem JSON 구조 변경 없이 사용 가능 |
| 구현 난이도 | 직관적이고 Spring Boot + Redisson에서 쉽게 구현 |
| 동시성 보장 | 멀티 인스턴스 환경에서도 안정성 확보 |
| 유지보수 | 다른 캐시 항목에도 동일한 패턴 적용 가능 |
| 확장성 | 향후 TTL 관리, lock timeout 등 쉽게 확장 가능 |
결론
👉 Redisson 분산 락은 구조 변경 없이도 높은 안정성과 일관성을 확보할 수 있는 가장 현실적인 전략이다.
- Race condition 방지
- JSON 캐시 일관성 유지
- 코드 가독성 및 유지보수 용이
- 멀티 인스턴스 환경에서 강력한 동시성 제어
끝.
'카카오테크 부트캠프' 카테고리의 다른 글
| Kafka 기반 SSE 알림 시스템 아키텍처 설계기 (3) | 2025.07.25 |
|---|---|
| DB 부하 98% 감소, 캐시 적중률 87%: 트래픽 집중형 시스템의 Redis 도입기 (3) | 2025.07.18 |
| Redis 캐시, Cache Stampede 방지 전략 보고서 (TTL Jitter, Mutex Locking) (0) | 2025.07.13 |
| Redis 캐시 키 설계 관련 보고서: 사용자 domain 정보 처리 전략 (1) | 2025.07.11 |
| SSE 도입 후 궁금증 완전 해소 – Polling 한계 극복부터 Nginx 설정, Tomcat 병목까지 총정리 (1) | 2025.06.29 |