pile·
백엔드·여기어때 (GC컴퍼니)여기어때 (GC컴퍼니)·

Spring Data Redis: Repository vs RedisTemplate 실전 성능 비교

피크 타임에 Redis CPU 사용률이 100%를 유지하면서 응답 시간이 평소의 3~5배까지 증가하는 장애를 겪었다. 원인을 추적한 결과 Spring Data Redis의 @RedisHash 기반 Repository 패턴이 복잡한 중첩 객체(1,600~2,000개 필드)를 저장할 때 HMSET·SADD·인덱스 연산을 다중 실행하는 구조적 특성이 문제였다. RedisTemplate으로 전환해 JSON 직렬화 단일 SET 명령으로 바꾸자 저장 속도가 2.45배 빨라지고 CPU가 100%에서 10%로 급락했다.

핵심 포인트
  • Spring Data Redis Repository(@RedisHash)는 객체 저장 시 HMSET(전체 필드) + SADD(Set 관리) + 인덱스 키 생성을 다중 명령으로 실행한다.
  • 필드 수가 1,600~2,000개에 달하는 중첩 객체를 저장하면 단일 save()가 수천 개의 Redis 명령을 유발할 수 있다.
  • RedisTemplate은 단일 SET 명령에 JSON 직렬화 값을 담아 저장해 명령 수를 대폭 줄인다.
  • 벤치마크: RedisTemplate이 Repository 대비 2.45배 빠른 저장 성능, Redis CPU 100% → 10% 감소.
  • 단순 캐시 목적(전체 객체 읽기/쓰기)에는 RedisTemplate, Redis 레벨 필드 검색·인덱스 기능이 필요하면 Repository가 적합하다.
상세 정리
  • 현상: 피크 타임 Redis CPU 100% 유지, 응답 시간 3~5배 증가 장애 발생.
  • Spring Data Redis Repository 동작 구조: @RedisHash 어노테이션 객체를 저장하면 내부적으로 Redis Hash 명령 사용. 1,600개 필드 객체의 경우 HMSET으로 1,600개 필드를 한번에 전송, 이후 SADD로 Set 구조에 키 추가, 추가 인덱스 연산 수행.
  • Repository 추가 오버헤드: @Indexed 어노테이션이 있는 필드마다 역인덱스 Set을 별도 관리. 객체 복잡도에 비례해 Redis 명령 수가 선형 증가.
  • RedisTemplate 동작 구조: ObjectMapper로 객체를 JSON 바이트 배열로 직렬화 후 단일 SET 명령으로 저장. Redis에서는 불투명한 바이트 blob으로 처리.
  • 구조적 트레이드오프: Repository는 Redis 내 필드별 접근(HGET), 인덱스 기반 검색 등 고급 기능을 제공하지만 쓰기 비용이 높음. RedisTemplate은 전체 객체 읽기/쓰기는 빠르지만 Redis 레벨 필드 접근이나 인덱스 검색 불가.
  • 벤치마크 결과: 저장 성능 2.45배 향상(RedisTemplate 기준), Redis CPU 100% → 10% 감소.
  • 마이그레이션 방향: Repository에서 RedisTemplate으로 전환, 불필요한 Set 구조와 보조 인덱스 제거.
  • 선택 기준: 단순 캐시 목적(전체 객체 읽기/쓰기)이면 RedisTemplate, Redis 레벨 필드 검색·인덱스 기능이 필요하면 Repository — 실제 워크로드에 맞게 선택해야 함.
  • 교훈: Spring Data Redis의 편의 추상화는 내부적으로 상당한 Redis 명령 오버헤드를 숨기고 있으며, 성능 민감한 경로에서는 저수준 API 사용을 검토해야 한다.
왜 읽나Spring Data Redis의 Repository 추상화가 고필드 객체에서 얼마나 큰 성능 부담을 유발하는지 실측 수치로 확인하고, RedisTemplate으로의 마이그레이션 근거와 방법을 파악할 수 있다.
여기어때 (GC컴퍼니)
여기어때 (GC컴퍼니) 블로그
원문은 여기서 이어서 읽을 수 있어요
원문 읽기
읽음 (0)

이 글과 비슷한

  1. 백엔드·여기어때 (GC컴퍼니)여기어때 (GC컴퍼니)·

    트랜잭션 스크립트에서 숙소 메타 + 가격 계산 모듈로 — 전시 아키텍처 개선기 (2/3)

    여기어때 전시개발팀이 숙소 상세(PDP) API를 해부한 결과, 코드상으로는 DB 호출 3번처럼 보이던 요청이 실제로는 MongoDB $lookup 체인으로 컬렉션을 19회 접근하는 구조였다. 이 트랜잭션 스크립트 방식의 핵심 문제는 "aggregation이 I/O를 가린다"는 점으로, 독립적인 쿼리 10개가 단일 파이프라인에 직렬화되어 병렬화 기회를 잃고, 가격 때문에 거의 안 바뀌는 이미지까지 매 요청마다 읽어야 하는 읽기 증폭이 발생했다. V3에서는 "조회 시점 조립"을 "쓰기 시점 사전 조립"으로 전환하고, 화면별로 복제되던 가격 계산 로직을 goodsprice 단일 모듈로 수렴했다. 4개 API(PLP/PDP/RDP/ILP)의 반복 마이그레이션은 Claude Code skill로 절차를 고정하고 쉐도잉 + 동일성 검증으로 안전망을 마련하는 방식으로 진행됐다.

    #architecture#migration#caching+2