seaking110 님의 블로그
캐시 본문
캐시
- 느린 저장소 앞에 위치한 빠른 저장소
- CPU 나 DB 앞에 존재
캐시의 대표적인 오픈 소스 서비스가 Redis
- 단순한 구조와 in-memory 저장소로 인한 빠른 성능 때문에 캐시에 주로 쓰임
캐시 전략과 정책
- 캐시 적중/미스
- Cache Hit VS Cache Miss
- Cache Hit
- 사용자가 요청한 데이터가 이미 캐시에 존재하는 경우
- 이 경우 데이터를 빠르게 반환 가능
- 사용자가 특정 유저 정보를 요청했을 때 해당 유저 데이터가 Redis에 이미 저장되어 있다면 바로 반환
- 응답 속도가 매우 빠름
- 백엔드 시스템의 부하가 줄고 트래픽 비용이 절감
- Cache Miss
- 요청한 데이터가 캐시에 없을 경우를 의미
- 이때는 원본 저장소에서 데이터를 가져오고 일반적으로 캐시에 저장한 뒤 반환
- Cold Miss (콜드 미스)
- 캐시에 처음 저장하는 경우
- 서버 시작 직후 혹은 새로운 키 요청 시 발생
- Capacity Miss(용량 미스)
- 캐시 공간이 부족해 오래된 데이터가 제거 된 뒤 다시 해당 데이터가 요청
- Expiration Miss (만료 미스)
- TTL이 지나 데이터가 만료된 뒤 다시 요청될 때
- 응답 속도가 느림
- 원본 저장소에 부하 발생
- 첫 사용자 경험이 다소 느림
- Hit Ratio
- 캐시가 얼마나 효율적으로 동작하고 있는지를 보여주는 지표
- 계산 공식 -> Hit Ratio = Number of Cache Hits / Total Requests
- 높은 적중률이 반드시 좋은건 아님
- Cache Hit
- Cache Hit VS Cache Miss
- 캐시 무효화 전략
- 캐시에 저장된 데이터가 더 이상 유효하지 않을 때, 캐시에서 삭제하거나 갱신하는 전략
- 데이터를 변화하지만 캐시는 한번 저장되면 변하지 않음
- 그렇기 때문에 캐시와 실제 데이터 사이에 불일치 문제가 발생할 수 있음
- 실무에서 자주 쓰는 캐시 무효화 전략
- TTL
- 캐시에 데이터를 저장할 때 일정 시간 이후 자동으로 만료되도록 설정
- 게시판 목록, 인기글, 날씨 정보처럼 짧은 시간 내 자주 바뀌지 않는 데이터에 사용
- 구현이 간단하고 확장성이 좋음
- Redis/Memcached에서 기본 지원
- LRU
- 가장 오랫동안 사용되지 않는 데이터를 제거
- 최근에 사용된 데이터는 앞으로도 사용될 가능성이 높다라는 가정
- 캐시에 접근한 시간을 기준으로 가장 오래 전에 접근된 데이터를 제거
- Redis에서 volatile-lru 정책
- OS 페이지 교체 알고리즘, 브라우저 캐시
- 구현 쉬움 (LinkedHashMap, Deque 로도 가능)
- LFU
- 가장 사용 횟수가 적은 데이터 제거
- 데이터 마다 접근 횟루를 기록하고 그 수가 가장 낮은 항목을 제거
- 추천 알고리즘 캐시
- Redis의 allkeys-lfu 정책
- 검색어 자동완성 캐시
- 자주 쓰는 데이터는 오랫동안 유지됨
- FIFO
- 가장 먼저 들어온 데이터를 제거
- 데이터가 들어온 순서만 고려
- 가장 오래된 삽입 데이터부터 제거
- 구현이 단순하나 Hit Ratio가 낮아질 수 있음
- 빠르게 회전하는 데이터에서 사용 (로그)
- TTL
- 캐시 전략
- Look-Aside
- 데이터를 찾을 때 우선 캐시에서 찾고 있으면 캐시에서 가져오고 없으면 DB에서 데이터를 가져오고 캐시에 넣어주는 작업 진행
- 이 구조는 캐시가 다운되더라도 DB에서 데이터를 가져올 수 있음
- 이런 경우 DB에서 캐시로 데이터를 미리 넣어주는 작업을 하기도 하는데 이를 Cache Warming
- Write-Through
- 데이터를 저장할 때 먼저 캐시에 저장한 다음 DB에 저장하는 방식
- 캐시는 항상 최신 정보를 가지고 있지만 저장이 느림
- 저장한 데이터가 재사용 되지 않을 수 도 있는데 무조건 캐시에 넣어버리기 때문에 리소스 낭비가 발생
- 이를 방지하기 위해 캐시에 유효기간을 설정하기도 함
- Write-Back
- 캐시에 데이터를 저장했다가 특정 시점마다 DB에 저장하는 방식
- 캐시에 데이터를 모았다가 한번에 DB를 저장하기 때문에 DB 쓰기 비용을 절약할 수 있지만 데이터를 옮기기 전에 캐시 장애가 발생하면 데이터 유실이 발생
- Write-Around
- 모든 데이터를 DB에 저장하고 읽은 데이터만 캐시에 저장되는 방식
- Cache Miss가 발생하는 경우에만 데이터를 저장하기 때문에 캐시와 DB 데이터가 다를 수 있음
- Look-aside와 Write-through랑 같이 씀
- Look-Aside
- 실무 관점으로 본 일관성과 동기화
- Stale date 문제
- 캐시에 저장된 데이터가 원본 데이터와 다르게 오래된 상태
- 일관성 붕괴 : 유저 경험 혼란
- 신뢰도 하락 : 관리자는 변경하였는데 사용자 화면에는 반영 X
- 특히 금전, 보안 관련 정보에서는 심각한 오류로 이어질 수 있음
- 캐시 갱신 타이밍
- 원본 데이터가 변경됐을 때 언제 어떻게 캐시를 갱신할 것인가를 결정
- Stale date 문제
- 캐시에 저장된 데이터가 더 이상 유효하지 않을 때, 캐시에서 삭제하거나 갱신하는 전략
방식
|
설명
|
장점
|
단점
|
쓰기 시 무효화 (Write-through / Cache Aside)
|
DB에 쓰는 순간 캐시도 함께 삭제/갱신
|
일관성 높음
|
동기 처리 시 느릴 수 있음
|
TTL 기반 자동 만료
|
일정 시간이 지나면 자동으로 캐시 삭제
|
구현 쉬움
|
그 전까지는 stale risk
|
비동기 갱신 (Pub/Sub 등)
|
Data 변경 시 캐시 갱신 메세지 브로드캐스트(subscribe, publish) 가능함
|
경량화가 되어있음, 실시간 분산 캐시 무효화 방식이 가능함
|
|
- 실무에서는
- 게시글 수정 시
- post:1234 캐시 → DB 저장 후 즉시 삭제 (redisTemplate.delete(...))
- 유저 랭킹은 5분마다 주기적으로 갱신 (@Scheduled 등으로 캐시 refresh)
- 게시글 수정 시
- 실무 적용 예시
- Spring + Redis
- 로그인한 유저 정보, 인기 게시글, 상품 리스트, 공통 설정 값 등
- CDN (Cloudflare, AWS CloudFront)
- 정적 리소스를 사용자에게 빠르게 전달
- 엣지 서버에 캐시된 리소스를 사용자에게 바로 응답
- S3 + CloudFront 연결
- 글로벌 응답 속도 향상
- DB 쿼리 결과 캐싱
- 복잡하고 비용이 큰 쿼리를 반복 실행하지 않기 위해 사용
- 랭킹, 인기글 리스트, 통계 등
- 서비스 단에서 캐시 로직 직접 구현
- API 응답 캐싱
- 같은 API 요청에 대해 서버 리소스를 반복 소비하지 않기 위해 사용
- 사용 대상으론 공공 정보 API, 뉴스 블로그 등
- 개인화된 응답에는 캐싱하면 안됨!
- Spring + Redis
주의 사항 및 캐시 안티패턴
- 캐시 중독
- 캐시가 없으면 시스템이 동작하지 않는 구조
- DB 대신 캐시만 보고 동작하도록 만들어서 Redis 장애 시 시스템 전체 장애 발생
- 캐시에만 저장하고 DB에 반영 안되는 방식에서 캐시 손실 시 데이터 증발
- 과도한 캐시 계층화
- 캐시를 너무 많은 레이어에 나눠두면 일관성 유지가 어려워지고 디버깅 복잡
- 캐시 삭제 시 모든 레이어에 반영해야해서 갱신 로직이 매우 복잡
- 만료 정책 미비로 인한 Stale 데이터
- TTL이나 무효화 로직이 제대로 없으면 오래된 데이터를 계속 보여줄 수 있음
- 접근 권한이 변경 되었을 때 접근 권한 캐시 때문에 접근이 가능한 경우
- 파레토 법칙에 따라 일부만 저장해도 대부분의 데이터를 커버할 수 있음
'Today I Learned' 카테고리의 다른 글
도커 실습 (0) | 2025.03.28 |
---|---|
Docker & CICD (0) | 2025.03.26 |
AWS 특강 (0) | 2025.03.24 |
plus 주차 트러블 슈팅 (0) | 2025.03.21 |
plus 주차 개인 과제 (0) | 2025.03.21 |