상품 상세 API가 느려져서 p95가 1초를 넘기 시작했다. 회의에서 "레디스 캐시 붙이면 되지 않아요?"라는 말이 제일 먼저 나오는 상황.
언제: 느린 원인이 특정 쿼리 몇 개일 때. EXPLAIN 떠보면 대부분 풀스캔이나 N+1이라서, 인덱스 하나 추가하거나 조인 한 번으로 끝나는 경우가 의외로 많다.
비용: 인덱스가 늘면 쓰기가 약간 느려지고, 쿼리 구조를 바꾸면 코드 리뷰와 배포가 필요하다. 그래도 캐시 무효화 버그를 쫓는 것보다 훨씬 싸다.
언제: 쿼리는 이미 최적인데 같은 결과를 초당 수천 번 읽어서 DB가 눌릴 때. 읽기가 쓰기보다 압도적으로 많고, 몇 초 오래된 데이터를 보여줘도 비즈니스가 괜찮다고 할 때만.
비용: 무효화 로직이라는 새로운 버그 종류가 생긴다. 오래된 데이터, 캐시 스탬피드, 그리고 Redis라는 운영 대상이 하나 늘어난다. "왜 캐시랑 DB가 다르지?"는 디버깅이 제일 오래 걸리는 류의 문제다.
기본은 캐시 말고 쿼리부터 고친다. EXPLAIN으로 원인을 눈으로 확인하고 인덱스와 N+1을 먼저 잡는다. 쿼리가 이미 최적인데도 같은 데이터를 읽는 트래픽이 DB를 누르고 있고, 약간 오래된 데이터를 보여줘도 된다는 합의가 있을 때만 캐시를 단다.
- 인덱스·N+1 다 잡았는데도 p99가 500ms 위에 머물고, 상위 10개 키 조회가 읽기 QPS의 80% 이상
- 피크 시간에 DB CPU 80% 초과 + 커넥션 풀 대기 시작 (쿼리 하나 문제가 아니라 총 읽기량 문제라는 신호)
- 읽기:쓰기 비율이 100:1 이상인데 매 요청이 DB까지 내려가고 있음
- 트래픽 2~3배 이벤트(세일, 프로모션)가 예정돼 있는데 DB 스케일업으로는 비용이 안 맞음
🔧EXPLAIN ANALYZEpg_stat_statementsRedisGrafana / Datadog APM (p99, DB CPU)
“I fix the query first — I only add a cache once it's already optimal and reads still crush the DB.”
checking microphone…