
1. 문제 - DB 병목 현상 및 성능 저하
- 모든 읽기(조회)와 쓰기(저장) 작업이 단일 데이터베이스(DB)에서 수행되면, 트래픽 증가 시 심각한 병목 현상이 발생
- 높은 동시 요청 처리 시 DB 부하 증가, 응답 속도 지연, 트랜잭션 충돌 증가, 서비스 장애 가능성 증가
- 캐싱(Redis)이 적용되어도, 근본적인 읽기/쓰기 처리 구조를 개선하지 않으면 한계가 존재
2. 원인 - 단일 DB 구조의 한계 및 CQRS 미적용
- 읽기(SELECT)와 쓰기(INSERT, UPDATE, DELETE) 요청을 같은 DB에서 처리함으로써 트랜잭션 경쟁 발생
- 읽기 트래픽(조회)이 과도할 경우, 쓰기 성능이 저하됨
- Redis 캐싱이 적용되더라도 DB 요청을 완전히 줄이지 못함 → 근본적인 읽기/쓰기 분리가 필요함
- CQRS(Command Query Responsibility Segregation) 미적용
→ 읽기(조회)와 쓰기(데이터 변경) 요청이 동일한 서비스 로직에서 수행
→ 마이크로서비스 및 대규모 트래픽 환경에서 확장성이 떨어집니다.
3. 해결 - CQRS 패턴 + DB 이중화 + Redis 캐싱 적용
1. CQRS 패턴 적용 (읽기/쓰기 서비스 분리)
- 읽기(조회)와 쓰기(저장) 로직을 분리하여 독립적으로 운영
- 쓰기(INSERT, UPDATE, DELETE) → Master DB에서만 수행
- 읽기(SELECT) 요청 → Slave DB(Replication)에서 수행
- 비즈니스 로직을 Command(쓰기)와 Query(조회)로 나누어 확장 가능하게 만들었습니다.
2. DB 이중화 (Master-Slave Replication)
- 쓰기(Write) 요청은 Master DB에서 처리
- 읽기(Read) 요청은 Slave DB(Read-Only) 또는 Redis 캐싱을 통해 분산 처리
- @Transactional(readOnly = true) 적용 시 자동으로 Slave DB로 분배됨
- 로드밸런서(Read/Write Split) 활용하여 트래픽 균형 유지
3. Redis 캐싱 적용으로 성능 최적화
- 자주 조회되는 데이터(공지사항, 프로모션, 인기 데이터 등)를 Redis에 캐싱
- 캐시 만료 정책(TTL) 적용하여 최신 데이터 유지
- 캐시 적중률(Cache Hit Ratio) 극대화하여 DB 부하 감소
- 읽기 요청을 캐싱하여 Slave DB에도 부담을 덜어줌
4. 부하 테스트(with JMeter)

1. 평균 응답시간(Average Response Time) 개선
- 미사용 : 평균 응답 시간 496ms
- 사용 : 평균 응답 시간 84ms
- 개선 효과 : 496ms -> 84ms 약 83.06% 개선
2. 에러율(Error Rate) 감소
- 미사용 : 에러율 42.52%
- 사용 : 에러율 6.28%
- 개선 효과 : 42.52% -> 6.28% 약 85.23% 감소
3. 처리량(Throughput) 향상
- 미사용 : 137.4 requests/sec
- 사용 : 337.9 requests/sec
- 개선 효과 : 처리량 약 2.46배 증가
5. 평가 - 성능 개선 및 시스템 안정성 향상
1. 읽기/쓰기 분리로 DB 부하 감소 및 트랜잭션 최적화
- 쓰기(INSERT, UPDATE, DELETE)는 Master DB, 읽기(SELECT)는 Slave DB로 분산
- Master DB 부하 감소, 쓰기 성능 유지 및 안정적인 운영 가능
2. CQRS 패턴 적용으로 확장성 향상
- 읽기/쓰기 서비스가 분리되어 마이크로서비스 아키텍처 확장 용이합니다.
- 개별적인 최적화 가능 → 읽기(조회) 서비스는 캐싱 강화, 쓰기 서비스는 트랜잭션 처리 최적화
3. 응답 속도 개선 및 사용자 경험 향상
- Slave DB를 활용한 읽기 부하 분산으로 응답 속도 단축
- Redis 캐싱을 활용하여 자주 조회되는 데이터는 DB 접근 없이 빠르게 제공
4. 장애 대응 및 서비스 안정성 증가
- Master DB 장애 발생 시, Slave DB를 승격(Promotion)하여 서비스 지속
- Failover 자동화를 통해 다운타임 최소화
5. 비용 절감 및 인프라 최적화
- 수직 확장(Scale-up) 대신 수평 확장(Scale-out) 가능 → 서버 비용 절감
- Slave DB 추가 확장 가능 → 트래픽 증가 시 새로운 Slave DB 추가로 대응
결론
- 읽기/쓰기 분리를 통해 DB 부하를 효과적으로 줄이고, 확장성이 뛰어난 아키텍처를 구축할 수 있었다.
- Redis 캐싱을 함께 활용하면 조회 성능을 극대화하면서도 DB 요청을 줄일 수 있습니다.
- 결과적으로, 트래픽 증가에도 안정적인 서비스 제공이 가능하며, 성능과 비용을 최적화할 수 있었습니다.
'TIL' 카테고리의 다른 글
B Tree 계열의 알고리즘을 활용한 DB 인덱싱 튜닝 (0) | 2025.02.28 |
---|---|
Redis 캐싱 전략 (0) | 2025.02.25 |
Spring Security6를 활용한 인증 인가 (1) | 2024.12.27 |
Burp Suite 인터셉터를 활용한 웹사이트 보안 테스트 (1) | 2024.12.27 |
Spring AOP와 예외처리를 활용한 로그 쌓기 (0) | 2024.12.27 |