ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 캐싱 최적화: Redis를 사용하지 않고 Local Cache로 캐싱하는 방법
    Spring 2025. 8. 25. 17:29

    문제 배경

    현재 진행 중인 프로젝트는 카드, 파일, 암호화 키, 공급자 정보, 카드 상품 등 다양한 프로파일 데이터를 빈번하게 조회해야 합니다.

    기존 방식은 DAO를 통해 매번 DB에 접근하는 구조였는데, 이로 인해 다음과 같은 문제가 있었습니다.

    • 동일한 데이터라도 매번 DB I/O 발생
    • 대량 프로파일 조회 시 DB 부하 증가
    • 운영 환경에서 빠른 응답 속도 보장 어려움

    이를 해결하기 위해 데이터를 미리 캐싱하는 방법을 도입하기로 결정했습니다.

     

    해결 전략

    1. Local Cache (In-Memory)

    Spring Service 내부에 Map<String, Entity> 또는 CaffeineCacheManager와 같은 In-Memory 캐시를 두고, 애플리케이션 시작 시 필요한 데이터를 메모리에 로드하여 사용하는 방식입니다.

    • 장점
      • O(1) 조회 속도로 빠른 응답 제공
      • 구현이 단순하며 외부 인프라 의존성이 없음
      • 애플리케이션 내부에서 즉시 활용 가능
    • 단점
      • 서버 인스턴스별 캐시가 독립적으로 존재 -> 멀티 서버 환경에서는 불일치 발생 가능
      • 서버 재시작 시 캐시 초기화 필요
      • 데이터 규모가 커질 경우 JVM Heap 및 GC 부담 증가

    2. Redis 기반 분산 캐시 (Global Cache)

    애플리케이션 외부에 Redis 서버를 두고, 모든 인스턴스가 동일한 캐시를 공유하여 사용하는 방식입니다.

    • 장점
      • 모든 서버 인스턴스가 동일한 캐시를 공유하여 데이터 일관성 보장
      • 서버 재시작 이후에도 Redis persistence 옵션으로 캐시 유지 가능
      • 애플리케이션 JVM 메모리 사용량 절감
    • 단점
      • 네트워크 I/O가 발생하므로 Local Cache보다 상대적으로 느림
      • Redis 장애 발생 시 서비스 전반에 영향을 미침
      • Sentinel/Cluster 구성 등 추가 인프라 관리 필요

    CaffeineCacheManager 도입

    이번 캐싱 전략에서는 Local Cache를 활용한 CaffeineCacheManager 방식을 도입하여, In-Memory 캐시를 구현했습니다.

    Local Cache 방식에서 대표적으로 ConcurrentMapCacheManager, Ehcache, CaffeineCacheManager 방식이 있습니다.

    그 중 Ehcache, CaffeineCacheManager 방식 사이에서 고민을 했지만 결론적으로 CaffeineCacheManager 방식을 채택하였습니다. 그 이유 Ehcache와 CaffeineCacheManager 두 라이브러리는 모두 JVM 기반 캐시를 제공하지만, 성능 측면에서 큰 차이가 있습니다.

    아래 벤치마크 결과를 보면 다음과 같습니다.

    Read 100%
    Read (75%) / Write (25%)
    Write (100%)

     

    • 읽기 전용(100%) 시: Caffeine은 Ehcache 대비 20배 이상 빠름
    • 읽기 75% / 쓰기 25% 혼합 시: Caffeine은 Ehcache 대비 10~20배 빠름
    • 쓰기 전용(100%) 시: Caffeine은 Ehcache 대비 4~10배 빠름

    순수 성능만 놓고 보면 Caffeine이 Ehcache 보다 훨씬 좋은 성능을 보이고 있습니다. 특히 동시성이 중요한 서비스에서 Caffeine은 안정적이고 빠른 성능을 보입니다. 반면, Ehcache는 off-heap 지원, 디스크 저장, 분산 캐시 등 엔터프라이즈 기능에서 강점을 가지고 있습니다.

    이번 프로젝트에서는 조회 성능을 목적으로 고려하고 있고 단일 애플리케이션에서 구현되기 때문에 성능적으로 우수한  Caffeine 방식을 채택했습니다.

     

    참고: 

    https://dalsacoo-log.tistory.com/entry/Local-Cache%EC%97%90-%EB%8C%80%ED%95%98%EC%97%AC-Spring-Cache-CaffeineEhcache-Redis-Client-side-caching

     

    Local Cache에 대하여 (Spring Cache, Caffeine/Ehcache, Redis Client-side caching..)

    글을 쓰게된 배경 Redis와 같은 Global Cache는 팀 내에서 거의 필수적으로 사용하고 있지만, Local cache는 자주 사용되지는 않고 있었습니다. 사실 Local cache는 서버마다 지역적으로 데이터를 저장해 관

    dalsacoo-log.tistory.com

    https://github.com/ben-manes/caffeine/wiki/Benchmarks

     

    Benchmarks

    A high performance caching library for Java. Contribute to ben-manes/caffeine development by creating an account on GitHub.

    github.com

     

    Redis 대신 Local Cache를 사용한 이유

    1. 시스템 복잡성 증가 최소화

    Redis를 운영하려면 Sentinel/Cluster 구성, persistence 설정, 장애 복구 전략 등 별도의 인프라 관리가 필요합니다. 하지만 이번 프로젝트의 목적은 단일 애플리케이션 레벨에서 캐시 계층을 도입하는 것이었기 때문에, 불필요한 운영 복잡성을 줄이는 것이 더 바람직했습니다.

     

    2. 데이터 특성에 적합

    캐싱 대상인 프로파일 데이터(카드, 암호화 키, 카드 상품 등)는 한 번 저장되면 변경이 거의 없고, 대부분 조회 위주로 사용됩니다. 따라서 서버 간 실시간 일관성이 필수적인 데이터가 아니므로, Local Cache만으로도 충분히 요구사항을 충족할 수 있었습니다.

     

    3. 비용/효율성 측면

    Redis는 별도의 서버 리소스(RAM, CPU, 네트워크)가 필요하지만, JVM 내부 캐시는 추가 인프라 비용 없이 애플리케이션 수준에서 바로 적용할 수 있습니다.

     

    Redis는 분산 환경에서의 캐시 일관성 보장과 같은 강점을 가지고 있지만, 이번 프로젝트의 요구사항에서는 불필요한 운영 복잡성을 피하고, 변경이 적고 조회 위주인 데이터 특성에 적합하며, 비용 효율성까지 얻을 수 있다는 점에서 Caffeine 기반 Local Cache 방식이 훨씬 더 합리적이라고 판단하였습니다.

    Spring Boot + Caffeine Cache vs DB 성능 비교 (k6)

    1. 테스트 목적

    서비스 내에서 자주 호출되는 읽기 전용 API는 매번 DB를 직접 조회하는 것보다 캐시를 활용하는 편이 성능과 안정성 측면에서 강점이 있습니다.
    이를 검증하기 위해, Spring Boot + MyBatis 환경에서 Caffeine Cache를 적용한 경우와 단순 DB 조회 방식을 비교하여 k6 부하 테스트를 진행했습니다.

     

    2. 테스트 시나리오

    k6의 constant-arrival-rate 모드를 사용하여 시나리오를 구성했습니다.

    • DB 시나리오: 0~30초, 초당 50 요청
    • Cache Warm-up: 30~40초, 초당 200 요청
    • Cache 시나리오: 40~70초, 초당 50 요청
    export const options = {
      scenarios: {
        db_scenario: { rate: 50, duration: "30s", exec: "db_test" },
        cache_warmup: { rate: 200, duration: "10s", exec: "cache_warm", startTime: "30s" },
        cache_scenario: { rate: 50, duration: "30s", exec: "cache_test", startTime: "40s" },
      },
      thresholds: {
        "http_req_duration{scenario:db_scenario}": ["p(95)<2000"],
        "http_req_duration{scenario:cache_scenario}": ["p(95)<2000"],
        "http_req_failed": ["rate<0.01"],
      },
    };

     

    3. 테스트 결과  

    구분 평균 응답시간 p90 응답 시간 p95 응답시간
    DB 조회 11.29ms 10.96ms 13.37ms
    Cache 조회 1.62ms 2.24ms 2.78ms

     

    [Cache 효과]

    • 동일 부하 조건에서 캐시를 적용한 결과, 응답 시간이 최대 7배 단축되었습니다.
    • 특히 p95 응답 시간은 16.58ms에서 2.78ms로 약 6배 개선되는 효과를 확인했습니다.

    마인드맵 내용 정리

    캐싱 성능 최적화 마인드맵 정리

    결론

    이번 프로젝트에서 캐싱을 도입하며 Spring Boot가 제공하는 다양한 캐싱 방식(Ehcache, Caffeine 등)의 특징과 성능 차이를 비교할 수 있었습니다. 실험 결과, Caffeine 기반 Local Cache를 적용했을 때 동일한 부하 조건에서 평균 및 p95 응답 시간이 6~7배 단축되었으며, 이를 통해 반복적인 DB 접근을 줄이고 DB 리소스 사용량과 시스템 부하를 동시에 감소시킬 수 있음을 확인했습니다.

    Reference

Designed by Tistory.