1. 배경.
현재 Redis를 활용해 특정 가게의 상세 정보, 메뉴 리스트, 리뷰 데이터를 캐싱하여 성능을 최적화하고 있습니다. 하지만 데이터 변경 시 캐시 무효화 전략이 다음과 같은 문제를 야기할 수 있다.
- 전체 캐시 삭제의 비효율성:
- 가게 상세 정보나 메뉴의 일부 데이터가 변경되어도 전체 캐시를 무효화하는 방식으로 인해 필요 이상의 데이터가 삭제되고 재로드 된다.
- 데이터 일관성 문제:
- 여러 서버에서 동시 요청 시 캐시가 즉시 갱신되지 않아, 잘못된 데이터가 반환될 가능.
- TTL 기반 무효화 한계:
- 리뷰 데이터는 TTL로 무효화하지만, 빈번한 데이터 갱신 시 TTL 만료 전까지 오래된 데이터가 유지될 수 있다.
2. 문제
- 캐시 무효화 범위의 비효율성:
- 데이터 변경 시 필요 이상의 캐시가 무효화되거나, 일부 데이터가 잘못 무효화될 가능성이 있음.
- 다중 서버 환경에서 캐시 동기화 부족:
- 다중 서버 간 캐시 동기화가 이루어지지 않아 데이터 일관성 문제가 발생할 가능성.
- 재로딩 시간의 지연:
- Lazy Loading 방식으로 무효화된 데이터를 다시 로드하는 과정에서 사용자가 느낄 수 있는 응답 지연.
3. 개선 목표
- 변경된 데이터에 대한 선택적 무효화를 적용하여 필요한 범위 내에서만 캐시를 무효화.
- 다중 서버 환경에서 Pub/Sub 기반 캐시 동기화를 통해 데이터 일관성을 유지.
- 캐시 무효화 이후 Eager Loading을 통해 중요 데이터를 미리 로드하여 사용자 경험 개선.
4. 해결 방안
- 선택적 무효화
- 변경된 데이터의 범위를 정확히 파악하여, 전체 캐시가 아닌 변경된 데이터에만 영향을 미치는 키를 무효화.
- 예:
- 가게 상태 변경 시 store:{storeId}:details만 무효화.
- 메뉴 추가/수정 시 store:{storeId}:menus만 무효화.
- 리뷰 데이터 추가/삭제 시 해당 리뷰 페이지만 무효화: store:{storeId}:reviews:page:{pageNumber}.
- Pub/Sub 기반 캐시 동기화
- Redis Pub/Sub를 활용해 서버 간 캐시 무효화 메시지를 전파:
- 예: 메뉴 변경 시 invalidate store:{storeId}:menus 메시지를 다른 서버로 전송.
- Spring Data Redis의 RedisMessageListener를 활용:
- Redis Pub/Sub를 활용해 서버 간 캐시 무효화 메시지를 전파:
@Component
public class CacheInvalidationSubscriber {
@Autowired
private RedisTemplate<String, String> redisTemplate;
@PostConstruct
public void subscribe() {
redisTemplate.convertAndSend("cache-invalidation", "store:{storeId}:menus");
}
}
3. 캐시 재로딩(Eager Loading)
- 캐시가 무효화된 후 Lazy Loading 대신 중요한 데이터를 미리 채우기.
- 예: @CacheEvict 이후 데이터를 즉시 갱신:
@CacheEvict(value = "storeDetails", key = "'store:' + #storeId + ':details'")
public void updateStore(Long storeId, UpdateStoreRequest req) {
// DB 업데이트 후 캐시 재로드
StoreDetailsResponse updatedDetails = getStoreDetails(storeId);
redisTemplate.opsForValue().set("store:" + storeId + ":details", updatedDetails);
}
4. TTL 동적 설정
- 변경 빈도가 높은 데이터(예: 리뷰)는 짧은 TTL을 설정하여 데이터 변경 후 캐시 자동 갱신을 유도:
- yaml 코드 복사 spring: cache: redis: time-to-live: storeDetails: 600s # 10분 reviews: 300s # 5분
yaml
spring:
cache:
redis:
time-to-live:
storeDetails: 600s # 10분
reviews: 300s # 5분
5. 기대 효과
- 캐시 무효화 범위 축소: 변경된 데이터에만 영향을 미쳐 재로드 성능 최적화.
- 데이터 일관성 개선: Pub/Sub로 다중 서버 환경에서 캐시 동기화 문제 해결.
- 응답 속도 향상: Eager Loading을 통해 사용자 요청 시 데이터가 이미 준비된 상태로 제공.
- 유지보수성 향상: 캐시 키와 데이터의 매핑 관계를 명확히 정의하여 오류 가능성 감소.