TIL - 20251120

juni·2025년 11월 20일

TIL

목록 보기
183/316

1120 Spring Boot 성능 최적화: 분산 캐시와 Redis


✅ 1. 로컬 캐시(Local Cache)의 한계

  • 이전에 학습한 Spring의 @Cacheable은 기본적으로 애플리케이션 내부 메모리(e.g., ConcurrentHashMap)에 데이터를 저장하는 로컬 캐시 방식입니다.

  • 문제점 (분산 환경에서의 데이터 불일치):

    1. 로드 밸런서 뒤에서 여러 대의 서버(WAS 1, WAS 2)가 동작하는 수평 확장(Scale-out) 환경을 가정해봅시다.
    2. 사용자 A의 요청이 WAS 1로 가서, getProductById(1)의 결과를 WAS 1의 로컬 캐시에 저장합니다.
    3. 이후 사용자 B의 동일한 요청이 로드 밸런서에 의해 WAS 2로 전달됩니다.
    4. WAS 2는 자신의 로컬 캐시에 데이터가 없으므로, 다시 데이터베이스를 조회해야 합니다. (Cache Miss)
    5. 더 심각한 문제는, WAS 1에서 상품 1의 정보가 변경되어 DB와 WAS 1의 캐시가 갱신되었을 때, WAS 2는 여전히 오래된(Stale) 데이터를 캐시에 가지고 있게 됩니다.
    • 결론: 로컬 캐시는 각 서버 인스턴스 간에 데이터가 공유되지 않아, 데이터 일관성(Consistency) 문제가 발생하고 캐시 효율이 떨어집니다.

✅ 2. 분산 캐시 (Distributed Cache)의 필요성

  • 분산 캐시는 여러 서버 인스턴스들이 공유할 수 있는 별도의 중앙 집중식 캐시 서버(또는 클러스터)를 두는 방식입니다.

  • 동작 방식:

    • 모든 서버 인스턴스는 데이터를 읽거나 쓸 때, 자신의 로컬 메모리가 아닌 외부의 공용 캐시 서버에 접근합니다.
    • 이를 통해 어떤 서버가 요청을 처리하든 항상 동일한 최신 캐시 데이터를 사용할 수 있어, 데이터 일관성 문제를 해결할 수 있습니다.
  • 대표적인 분산 캐시 솔루션: Redis, Memcached


✅ 3. Redis: 고성능 인메모리(In-Memory) 데이터 저장소

  • Redis (Remote Dictionary Server)는 키-값(Key-Value) 구조의 데이터를 메모리에 저장하고 조회하는 인메모리 데이터 저장소입니다.

  • 핵심 특징:

    1. 매우 빠른 속도: 모든 데이터를 디스크가 아닌 메모리에서 처리하므로, 디스크 기반의 관계형 데이터베이스(RDBMS)보다 월등히 빠릅니다.
    2. 다양한 자료구조: 단순한 문자열(String) 외에도 Lists, Sets, Sorted Sets, Hashes 등 다양한 자료구조를 지원하여, 단순 캐싱뿐만 아니라 랭킹 보드, 세션 관리, 메시지 큐 등 다양한 용도로 활용될 수 있습니다.
    3. 영속성 (Persistence): 인메모리 데이터베이스이지만, 스냅샷(Snapshot)이나 AOF(Append Only File) 방식을 통해 데이터를 디스크에 저장하여, 서버가 재시작되어도 데이터를 복구할 수 있는 영속성 옵션을 제공합니다.

✅ 4. Spring Boot와 Redis 연동 (spring-boot-starter-data-redis)

  • Spring Boot는 spring-boot-starter-data-redis 의존성을 통해 Redis를 매우 쉽게 연동하고, Spring의 캐시 추상화(@Cacheable)와 통합할 수 있도록 지원합니다.

➕ 연동 절차

  1. 의존성 추가: build.gradlespring-boot-starter-data-redis를 추가합니다.

  2. application.yml 설정: Redis 서버의 접속 정보를 설정합니다.

    spring:
      data:
        redis:
          host: localhost
          port: 6379
      cache:
        type: redis # Spring의 캐시 관리자로 Redis를 사용하도록 지정
  3. @EnableCaching 활성화: 메인 애플리케이션 클래스에 @EnableCaching 어노테이션을 추가합니다.

  4. @Cacheable 사용: 기존과 동일하게 캐싱이 필요한 메서드에 @Cacheable 어노테이션을 붙입니다.

    @Service
    public class ProductService {
    
        // 이제 이 메서드의 결과는 로컬 메모리가 아닌,
        // 외부의 Redis 서버에 저장되고 조회됩니다.
        @Cacheable(value = "products", key = "#id")
        public Product getProductById(Long id) {
            System.out.println("DB에서 상품 정보를 조회합니다...");
            return productRepository.findById(id).orElse(null);
        }
    }
  • 결과: 단지 설정 변경만으로, 기존의 로컬 캐시 코드가 이제 분산 캐시로 동작하게 됩니다. 이것이 Spring의 강력한 추상화(Abstraction)의 힘입니다. 여러 서버 인스턴스들이 모두 동일한 Redis 서버를 바라보게 되므로, 분산 환경에서의 데이터 일관성 문제가 해결됩니다.

📌 요약

  • 단일 서버 환경에서는 로컬 캐시로 충분할 수 있지만, 여러 서버로 확장되는 분산 환경에서는 데이터 불일치 문제가 발생합니다.
  • 분산 캐시는 여러 서버가 공유하는 중앙 캐시 서버를 두어 이 문제를 해결하며, Redis가 가장 대표적인 솔루션입니다.
  • Redis메모리 기반으로 동작하여 매우 빠른 속도를 제공하며, 다양한 자료구조를 지원하는 다목적 데이터 저장소입니다.
  • Spring Boot에서는 spring-boot-starter-data-redis와 간단한 설정을 통해, 기존의 @Cacheable 코드를 변경하지 않고도 캐시 저장소를 로컬 메모리에서 Redis(분산 캐시)로 손쉽게 전환할 수 있습니다.

0개의 댓글