❐ Description
주로 고정적인 데이터나, 변화가 적은 데이터에 대해서 캐싱을 많이 사용해왔다. 물론 앞으로도 많이
사용할 것 같은데, Redis를 캐시로 더 잘 사용하는 법과 주의할 점에 대해서 알아보고 숙지하자.
❐ Redis와 캐시
1. Warming-up
캐시를 사용하게 되면 아래의 이점을 챙길 수 있다.
- 원본 데이터를 가지고 오는 시간을 단축 ➪ API latency 단축
- 애플리케이션 자체의 리소스 최적화
- RDB에 장애가 생기더라도 캐시에서 데이터를 가지고 올 수 있음 ➪ 장애 시간 단축
그렇다면 어떤 특지을 갖는 데이터를 대상으로 캐싱을 해야할까?
- 변화가 적은 데이터 (상단 배너 공고)
- 자주 검색되는 데이터(최근 검색 이력)
2. Redis as a Cache
그렇다면 Redis를 캐시의 용도로 사용하는 이유는 무엇일까?
- 구성 및 사용이 간단하다.
- 대부분의 자료구조를 제공한다.
- 데이터 검색 및 반환이 빠르다.
- 전체적으로 HA(고가용성)를 가진 솔루션 ➪ Sentinel, Cluster
- 수평적 확장이 용이하다.
3. Caching Strategy
Redis를 캐시로 사용할 때 어떻게 배치할 것인지에 따라 서비스의 성능에 큰 영향을 끼칠 수 있다.
🧩 읽기 전략 (look-aside) ⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯

가장 일반적인 배치 방법으로, 애플리케이션에서 데이터를 읽어갈 때 주로 사용하는 방법이다.
위 그림과 같이 Redis에 찾는 데이터가 있다면(Cache-Hit) MySQL 서버에서 데이터를 조회하지 않는다.
하지만, Cache-Miss가 된다면, 아래의 흐름으로 데이터를 받아올 것이다.

만약 Redis를 프로젝트에 처음 도입할 때 어떤 문제점이 있을까?
» 기존 RDB에 있던 모든 데이터에 대해 Cache Miss가 발생
위 문제를 해결하기 위해서 미리 데이터를 Redis로 밀어 넣어주는 Cache-Warming 작업을 수행해야 한다.
🧩 쓰기 전략과 캐시의 일관성 ⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯
Redis에 저장된 캐시 데이터는 단순히 RDB 데이터를 복사해 둔 값이다. 따라서 항상 원본 데이터와
동일한 값을 갖도록 유지해야 한다.
만약 원본 데이터가 변경되었는데 캐시 데이터가 변경되지 않아서 데이터의 불일치가 발생한다.
이를 캐시 불일치(Cache-inconsistency)라고 한다.
1. write through
» 데이터베이스를 업데이트할 때마다 매번 캐시에도 데이터를 함께 업데이트시키는 전략
- 항상 캐시가 최신 데이터를 가지게 된다.
- 매번 2개의 저장소에 저장돼야 하기 때문에 데이터 쓸 때마다 시간이 많이 소요
- 이경우 저장하는 데이터가 재사용되지 않을 수도 있다. 따라서 만료 시간을 설정해라.
2. cache invalidation
» RDB에 값을 업데이트할 때마다 캐시에서는 데이터를 삭제하는 전략
- 캐시 데이터를 삭제하는 것이 새로운 데이터를 저장하는 것보다 적은 리소스를 사용
- 🤔 그럼 이것도 결국 Cache Miss가 많이 발생할 것 같은데...?
3. write behind (write back)
» 먼저 Redis를 최신화하고, 비동기 적으로 RDB를 업데이트 하는 전략
- 쓰기가 많고, 저장되는 데이터가 실시간으로 정확한 데이터가 아닌 경우에 고려
- 예를 들어 유튜브 좋아요 수
- 5분 간격으로 좋아요수 집계 후 RDB update
- Redis에 문제가 생겨 캐시가 날아가는 위험성 감수
❐ 캐시에서의 데이터 흐름
당연한 이야기지만 레디스는 RDB 보다 적은 양의 데이터를 저장하는 임시 저장소다.
따라서 데이터베이스의 서브셋이라고 볼 수 있다.
1. 만료 시간 (Time To Live)
데이터가 얼마나 오래 저장될 것 인지를 나타내는 시간 설정이다. 다음과 같이 사용하면 된다.
127.0.0.1:6379> set gilbert 10
OK
127.0.0.1:6379> TTL gilbert
6
127.0.0.1:6379> TTL gilbert
-2

🧩 키 삭제 방식 ⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯
1. Passive 방식
- 클라이언트가 키에 접근하고자 할 때, 키가 만료 됐다면 삭제하는 방식
- 사용자가 접근할 때만 수동적으로 삭제됨
- 즉, 사용자가 접근하지 않으면 TTL이 만료됐더라도 삭제되지 않게 됨.
2. Active 방식
- TTL이 있는 값 중 20개를 랜덤으로 선택하여, 그 중 TTL이 만료된 키를 삭제하는 방식
2. 메모리 관리와 maxmemory-policy 정책
레디스는 TTL을 설정해서 데이터의 수명을 관리하고, 데이터가 자동으로 삭제되게 함으로써 메모리 관리를 한다.
하지만 레디스의 메모리를 제한적이기 때문에 모든 키에 만료 시간을 설정하더라도 너무 많은 키가 저장된다면
메모리가 가득 차는 현상이 발생할 수 있다.
따라서 레디스에서는 `maxmemory` 설정과, 이 용량이 초과할 때 처리 방식을 `maxmemory-policy` 설정 값을
사용해서 관리한다.
🧩 NO eviction (default) ⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯
데이터가 가득 차더라도 임의로 데이터를 삭제하지 않고
더 이상 레디스에 데이터를 저장할 수 없다는 에러를 반환하는 정책
- 데이터의 관리를 애플리케이션 측에서 하겠다는 것을 의미
- 권장하지 않는 방법
🧩 LRU(Least Recenty Used) eviction ⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯
레디스에 데이터가 가득 찼을 때 가장 최근에 사용되지 않은 데이터부터 삭제하는 정책
- 캐시는 나중에 사용될 가능성이 있는 데이터를 유지하는 것이 목표
🧩 LFU(Least-Frequenty Used) eviction ⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯
레디스에 데이터가 가득 찼을 때 가장 자주 사용 되지 않은 데이터부터 삭제하는 정책
- 키가 오랫동안 사용 되지 않았더라도 과거에 자주 액세스했던 키라면
- 나중에도 자주 사용될 수 있다는 가정하에 우선순위가 높아지게 된다.
🧩 RANDOM eviction ⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯
레디스에 저장된 키 중 하나를 임의로 골라내 삭제하는 정책
- 삭제될 키 값을 계산하지 않아도 된다는 점에서 레디스 의 부하를 줄여주는 방식
- 하지만 랜덤이기 때문에 자주 사용할 수 있는 데이터를 삭제할 수도 있다.
3. 캐시 스탬피드 현상
캐시 스탬피드란, TTL이 만료된 데이터에 대해서 여러 클라이언트가 동시에 요청을 보내게 되면
중복읽기(duplicate read)가 발생하고, 이 후 각 애플리케이션에서는 각 요청마다 읽어온 데이터를
레디스에 저장하게 되는데 이 때 중복쓰기(duplicate write)가 발생하는 현상을 말한다.
만약 조회하는 쿼리가 복잡한 통계 쿼리라고 한다면 DB에 부하를 줄 수 있고, 이는 곳 서버 이슈로 이어질 수 있다.
이를 개선하는 노력 끝에 현재는 2015년에 등작한 `PER 알고리즘`을 채택한다고 한다.
PER 알고리즘은 캐시 유효기간이 만료되기 전 일정확률로 캐시를 재연산하는 알고리즘이다.
관련해서 화해 기술 블로그를 참고해서 개념을 이해했다.
❐ 세션 스토어와 레디스
분산환경인 프로젝트에서는 여러 요소들을 생각해야 하는데, 그 중 session-store 하나이다.
1. Sticky Session
아래의 상황은 모든 사용자의 세션이 하나의 서버에 몰려있는 현상인 `Sticky Session` 을 표현한다.

이 경우에는 다른 서버에 요청할 경우 "다시 로그인" 하라는 등의 안내 문구가 나올 것이다.
2. Sticky Session 해결법
🧩 all-to-all ⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯
이 방법은 동일한 세션 정보를 모든 서버에 저장하는 방식이다.
당연하게도 불필요하게 중복되는 데이터가 생기기 때문에, 저장 공간을 낭비하는 방법이다.
🧩 DB as a Session Store ⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯
RDB에 세션 정보를 저장하는 방법이다. 이 방법은 개인적으로 별로라고 생각되는게,
사용자가 많은 서비스일 수록 그에 비례하여 DB 성능이 떨어질 것이기 때문이다.
🧩 Redis as a Session Store ⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯
Redis를 Session Store로 사용한다면, RDB 보다 빠르게 접근이 가능하며 TTL을 설정해서
Session을 관리할 수도 있다.
구글링을 하다보니 Redis 자체적으로 (서버에서 설정한) session 만료시간에 5분을 추가하는 이상현상이
있는 것을 알게 됐다. 이런 현상이 발생하는 이유는 세션의 접근성과 관리의 안정성을 보장하기 위해서라고 한다.
만약 사용자가 timeout을 10분으로 설정하면, 이는 논리적 만료 시간으로 간주된다.
하지만, Redis는 물리적 만료 시간을 15분(10 + 5)으로 설정한다. 논리적 만료 시간이 지나면 해당 세션은
유효하지 않아서 더는 접근할 수 없지만, 물리적 만료 시간까지는 Redis에 남아 있어 접근이 가능하다.
따라서 남은 5분 동안 Redis는 SessionDestroyedEvent나 기타 후처리를 안정적으로 실행할 수 있게 된다.
TODO : Gilboard에 구현
3. 캐시와 세션의 차이
캐시에 저장된 데이터는 여러 애플리케이션에서 함께 사용할 수 있다. 여러 애플리케 이션이 함께 사용할수록 더
효율적이다. 하지만 세션 스토어에 저장된 데이터는 여러 사용자 간 공유되지 않고, 특정 사용자 ID에 한해 유효하다.
추가적으로 Redis를 세션스토어의 역할로 사용할 때는 캐시로 쓸 때보다 신경을 더 써야한다.
예를 들어 아래의 상황을 생각해보자.
- 사용자가 장바구니에 아이템 5개 추가
- 로그아웃(세션 만료)
유저가 로그아웃할 때 세션은 종료되며 이때 데이터의 종류에 따라 데이터베이스에 저장 해 영구적으로 보관할
것인지, 삭제할 것인지가 결정되는데 session 데이터가 유실된다면 사용자가 다시 로그인 했을 때 장바구니는
텅 비어 있을 것이다.
'Back-End > Redis' 카테고리의 다른 글
Redis로 동시성 제어해보기 (0) | 2025.02.04 |
---|---|
레디스 데이터 백업 방식 (0) | 2025.01.08 |
Redis 자료구조 활용 사례 (0) | 2024.12.18 |
Redis 기본 개념 (0) | 2024.12.18 |
Redis 설정하기 (0) | 2024.12.17 |