TIL

DB 이중화 구현을 통한 DB 병목 현상 예방(with CQRS패턴 & Redis 캐싱 전략)

DongHo 2025. 2. 25. 17:27

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 요청을 줄일 수 있습니다.
  • 결과적으로, 트래픽 증가에도 안정적인 서비스 제공이 가능하며, 성능과 비용을 최적화할 수 있었습니다.