피드로 돌아가기
Dev.toBackend
원문 읽기
How I Built a Two-Level Cache to Serve Millions of Lookups in Under a Millisecond
상품 조회 서비스가 Caffeine(L1) + Redis(L2) 2단계 캐싱 도입으로 P50 레이턴시를 15ms에서 0.3ms로 감소
AI 요약
Context
Elasticsearch 기반 상품 조회 서비스의 tail 레이턴시가 80ms를 초과하며 시스템 병목이 발생했다. Redis 단일 캐싱만으로는 네트워크 홉당 1~5ms의 지연이 누적되면서 충분하지 않았고, 애플리케이션 내 캐싱만으로는 서비스 재시작 시 상태 유실과 인스턴스 간 공유 불가 문제가 발생했다.
Technical Solution
- Caffeine을 인프로세스 L1 캐시로 도입: W-TinyLFU 정책 기반 TTL 30~60초, 최대 5,000개 엔트리로 제한하여 핫 키에 집중
- Redis를 분산 L2 캐시로 도입: TTL 5~15분 설정하여 재시작 간 상태 보존 및 인스턴스 간 공유
- MessagePack 직렬화 사용: JSON 대비 약 32% 더 작은 페이로드로 Redis 메모리 및 역직렬화 비용 절감
- Cache-aside 패턴 구현: Caffeine 미스 시 Redis 조회 후 비동기 백필, Redis 미스 시 Elasticsearch 조회 후 양쪽 캐시에 기록
- 이벤트 기반 캐시 무효화: Pub/Sub을 통해 데이터 수정 시 모든 인스턴스의 Caffeine을 밀리초 단위로 무효화하여 30초 이상의 stale read 방지
Impact
- P50 레이턴시: 약 15ms에서 약 0.3ms로 감소 (약 98% 개선)
- Elasticsearch 요청 량: 피크 트래픽 시간대 70% 이상 감소
- 캐시 히트율: 양쪽 캐시 계층 통합 95% 이상 유지
Key Takeaway
높은 읽기 트래픽을 처리하는 시스템에서는 단일 캐싱 계층보다 2단계 캐싱이 네트워크 홉 제거와 상태 보존 사이의 트레이드오프를 효과적으로 해결한다. Caffeine의 서브밀리초 응답과 Redis의 크로스 인스턴스 상태 공유를 결합하면 source of truth 계층(Elasticsearch)의 부하를 대폭 줄이면서 일관성 위험을 제어할 수 있다.
실천 포인트
높은 접근률을 보이는 데이터를 다루는 JVM 기반 마이크로서비스에서 Caffeine L1(TTL 30~60초) + Redis L2(TTL 5~15분) + 이벤트 기반 무효화를 적용하면, 레이턴시 지연을 0.3ms 이하로 유지하면서 데이터 freshness를 30초 이내로 제어할 수 있다. MessagePack 직렬화로 Redis 메모리 오버헤드도 32% 감소시킬 수 있다.