Cache(캐시)
캐시란 효율적으로 엑세스할 수 있는 임시 데이터 저장소를 의미하는데, 다음 조건을 만족시키면 효과적으로 사용할 수 있다.
원본 데이터 저장소에서 원하는 데이터를 찾기 위해 검색하는 시간이 오래걸리거나, 계산을 통해 가져와야 하는 경우
캐시에서 조회하는 것이 원본 데이터 저장소에서 조회하는 것보다 빠른 경우
데이터의 변경이 적은 경우
자주 사용되는 데이터인 경우
캐시로서의 레디스
캐시로서의 레디스를 사용하면 다음과 같은 이점을 얻을 수 있다.
다양한 자료 구조를 간단하게 사용 가능
인메모리 데이터 저장소이기 때문에 빠른 속도로 데이터를 저장 및 조회 가능(평균 1ms 미만)
레디스 자체적으로 고가용성 솔루션을 제공으로 안정적인 서비스 운영 가능
클러스터링을 통한 스케일 아웃 가능
캐싱 전략
읽기 전략 - look aside
레디스 캐시를 사용할 때 가장 일반적으로 배치하는 방법으로, 절차는 다음과 같다.
애플리케이션이 찾고자 하는 데이터가 먼저 캐시에 있는지 확인
캐시에 데이터가 있으면 캐시에서 데이터를 조회(=캐시 히트)
찾고자 하는 데이터가 없으면(=캐시 미스) 데이터베이스에서 데이터를 조회
조회된 데이터를 캐시에 저장
look aside 구조의 장점과 단점은 다음과 같다.
장점: 레디스에 문제가 발생하더라도 데이터베이스에서 데이터를 조회하여 서비스 장애로 이어지지 않음
단점: 만약 레디스를 통해 데이터를 가져오는 연결이 매우 많으면 커넥션이 한 번에 데이터베이스로 몰려 데이터베이스에 부하가 걸릴 수 있음
이러한 구조를 lazy loading이라고 부르는데, 최초에 데이터베이스 데이터를 캐시에 넣어주는 작업을 캐시 워밍(cache warming)이라고 한다.
쓰기 전략과 캐시의 일관성
캐시는 데이터베이스에 저장돼 있는 단순 복사 값이기 때문에, 원본 데이터와 동일한 값을 유지하는 것이 중요하다. 데이터가 변경됐을 때 원본 데이터베이스에만 업데이트돼 캐시에 변경된 값이 반영되지 않는 것을 캐시 불일치(cache inconsistency)라고 한다.
이러한 불일치를 최소화와 성능을 고려한 쓰기 전략들은 다음과 같다.
write through: 데이터베이스에 데이터를 저장과 동시에 항상 캐시에도 업데이트시키는 방식
캐시에 항상 최신 데이터를 가져 불일치를 최소화
매번 쓰기 연산이 발생하므로 저장 시 속도가 느릴 수 있음
다시 사용될 만한 데이터가 아닌 경우 리소스 낭비
cache invalidation: 데이터베이스에 값을 업데이트할 때마다 캐시에서 데이터를 삭제하는 방식
새로운 데이터 저장보다 특정 데이터 삭제가 더 빠르기 때문에 쓰기 성능이 향상
write behind: 데이터베이스에 데이터를 저장하기 전에 먼저 캐시에 저장하고, 비동기적으로 캐시의 데이터를 데이터베이스에 저장하는 방식
쓰기 성능을 향상시키면서 캐시 불일치를 최소화
저장되는 데이터가 실시간으로 정확한 데이터가 아닌 경우 적합
데이터베이스에 저장되지 않은 데이터 손실이 발생할 수 있음
캐시에서의 데이터 흐름
기본적으로 캐시는 데이터 스토어가 갖고 있는 데이터 중 자주 사용되는 데이터를 캐시로 가져와 저장하는 역할을 한다. 하지만 메모리를 사용하는 캐시는 데이터를 저장하는 용량이 제한적이기 때문에, 캐시에서 데이터를 삭제하는 정책이 필요하다.
만료 시간
레디스에서는 TTL(Time To Live)을 사용하여 데이터의 만료 시간을 설정할 수 있다.
한 번 설정 된 만료 시간은 키의 이름을 바꾸거나 데이터를 조작하더라도 만료 시간은 변경되지 않지만, 새로운 값으로 키를 덮어 쓰면 만료 시간이 초기화된다.
만료 시 삭제 정책
레디스에서 키가 만료됐더라도 바로 메모리에서 삭제되는 것이 아니라, passive
방식과 active
방식 두 가지 방식으로 삭제된다.
passive: 클라이언트가 키에 접근하고자 할 때 만료됐을 경우 메모리에서 수동적으로 삭제
사용자가 다시 접근하지 않는 키가 존재할 수 있어 메모리를 낭비할 수 있음
active: 일정 주기마다 TTL이 존재하는 키 중 일정 갯수만큼 랜덤하게 뽑아낸 뒤, 만료된 키를 삭제(1초에 10번 씩 / 20개씩 랜덤으로)
메모리 관리와 maxmemory-policy 설정
레디스에서 키에 만료 시간을 설정하더라도 많은 데이터로 인해 메모리가 가득 차는 상황이 발생하게 되는데, 레디스는 내부 정책을 사용해 어떤 키를 삭제할지 결정한다.
최대 저장 용량을 설정하는 maxmemory
설정과 삭제 정책을 설정하는 maxmemory-policy
설정을 사용하여 메모리 관리를 할 수 있는데, 그 정책은 다음과 같다.
Noeviction(기본값): 데이터가 가득 차더라도 데이터를 삭제하지 않고 에러를 발생
로직에 따라 장애 상황으로 이어질 수 있어, 캐시로 사용할 때 권장하지 않는 설정값
데이터의 관리를 캐시에게 맡기는게 아닌, 애플리케이션에서 데이터를 관리하는 것이 좋을 때 고려 가능
LRU eviction: 가장 오랫동안 사용되지 않은 키를 삭제
volatile-lru / allkeys-lru 알고리즘 존재(레디스 공식 문서에서는 allkeys-lru를 권장)
LFU eviction: 가장 적게 사용된 키를 삭제
volatile-lfu / allkeys-lfu 알고리즘 존재
Random eviction: 랜덤하게 키를 삭제(사용하지 않는 것을 권장)
알고리즘을 사용하지 않아 삭제될 키 값을 계산하지 않아 부하가 적음
volatile-ttl: 만료 시간이 있는 키 중 만료 시간이 가장 적게 남은 키를 삭제
캐시 스탬피드
캐시 스탬피드(cache stampede)는 캐시에 대량의 요청이 동시에 들어와 캐시 미스가 발생하는 현상을 의미한다. 캐시 스탬피드가 발생하면 캐시 미스로 인해 데이터베이스에 부하가 걸리고, 서비스의 성능이 저하될 수 있는데, 그 과정과 이유는 다음과 같다.
애플리케이션에서 특정 데이터를 조회
캐시에 데이터가 없어 데이터베이스에서 데이터를 조회
데이터베이스에서 데이터를 조회하여 캐시에 저장하는 동안 다른 요청이 발생
아직 캐시에 데이터가 저장되지 않은 상태에서 다른 요청에 의해 데이터베이스에서 다시 조회
한꺼번에 많은 요청이 왔다면 데이터베이스에 많은 쿼리가 발생하여 부하가 걸릴 수 있음
캐시 스탬피드 방지 방법
캐시 스탬피드를 방지하기 위한 가장 간단한 방법은 만료 시간을 충분히 길게 설정하는 것이며, 다른 방법으로는 다음과 같은 방법들이 존재한다.
선 계산: 캐시가 만료되기 전 랜덤한 확률로 캐시를 갱신
PER 알고리즘: 랜덤 기반이나, 만료 시간이 가까워질수록 갱신 확률을 높이는 방법
참고자료
Last updated
Was this helpful?