1) 레디스와 캐시
(1) 캐시란?
- 캐시란 데이터의 원본보다 더 빠르고 효율적으로 액세스할 수 있는 임시 데이터 저장소를 의미한다.
위의 그림에서와 같이 사용자가 동일한 정보를 반복적으로 액세스할 때 원본이 아니라
캐시에서 데이터를 가지고 옴으로써 리소스를 줄일 수 있다.
(2) 캐시 도입을 통한 성능 개선
-애플리케이션이 다음 조건을 만족시킨다면 캐시를 도입했을 때 성능을 효과적으로 개선할 수 있다
(1) 원본 데이터 저장소에서 원하는 데이터를 찾기 위해 검색하는 시간이 오래 걸리거나,
매번 계산을 통해 데이터를 가져와야 한다
(2) 캐시에서 데이터를 가져오는 것이 원본 데이터 저장소 데이터를 요청하는 것보다 빨라야 한다
(3) 캐시에 저장된 데이터는 잘 변하지 않는 데이터다
(4) 캐시에 저장된 데이터는 자주 검색되는 데이터다.
- 위의 조건을 만족시키는 이상적인 캐시는 애플리케이션이 직면하게 되는 많은 문제점을 해결할 수 있다
우선 원본 데이터 저장소 데이터를 가지고 오는 시간을 단축시키기 때문에 애플리케이션의 응답 속도를
줄일 수 있다
2) 캐시로서의 레디스
(1) 사용의 편의성 및 다양한 자료 구조
- 레디스는 우선 사용이 간단하다는 장점이 있다.
단순하게 키-값 형태로 저장하므로, 데이터를 저장하고 반환하는 것이 굉장히 간단하며,
자체적으로 다양한 자료 구조를 제공하기 때문에 애플리케이션에서 사용하던, list, hash 등의
자료 구조를 변환하는 과정 없이 레디스에 바로 저장할 수 있다
(2) 인메모리 데이터 저장소
- 또한 레디스는 모든 데이터를 메모리에 저장하는 인메모리 데이터 저장소이기 때문에
데이터를 검색하고 반환하는 것이 상당히 빠르다는 특징을 갖고 있다
관계형 데이터베이스에서는 테이블의 특정 데이터를 찾으려면
우선 디스크에 접근해 데이터를 검색해와야 한다
- 하지만 레디스에서는 모든 데이터가 메모리 위에 존재하기 때문에
데이터에 접근하는 시간이 굉장히 빠르다
평균 읽기 및 쓰기 작업 속도가 1ms 미만이며, 초당 수백만 건의 작업이 가능함을 의미한다.
(3) 고가용성 및 확장성
- 레디스는 자체적으로 고가용성 기능을 가지고 있는 솔루션이라는 점도 큰 장점이다
일부 캐싱 전략에서는 캐시에 접근할 수 없게 되면 이는 곧바로 서비스의 장애로 이어질 수도 있다
- 따라서 캐시 저장소도 일반적인 데이터 저장소와 같이 안정적으로 운영될 수 있는 조건을 갖추는 것이 좋다
레디스의 센티널 또는 클러스터 기능을 사용하면 마스터 노드의 장애를 자동으로 감지해
fail-over를 발생시키기 때문에 운영자의 개입 없이 캐시는 정상으로 유지될 수 있어 가용성이 높아진다.
- 레디스의 클러스터를 사용하면 캐시의 스케일 아웃 또한 쉽게 처리할 수 있다
서비스의 규모에 따라 캐시 자체의 규모도 늘어나야 할 상황이 발생할 수 있는데,
자체 샤딩 솔루션인 클러스터를 사용하면 수평 확장이 굉장히 간단해진다는 장점이 존재한다.
3) 레디스 캐싱 전략
- 레디스를 캐시로 사용할 때 레디스를 어떻게 배치할 것인지에 따라 서비스의 성능에 큰 영향을 끼칠 수 있다.
캐싱 전략은 캐싱되는 데이터의 유형과 데이터에 대한 액세스 패턴에 따라 다르기 때문에
서비스에 맞는 적절한 캐싱 전략을 선택하는 것이 중요하다.
(1) 읽기 전략 - look aside
- 애플리케이션에서 데이터를 읽어갈 때 주로 사용하는 look aside 전략은
레디스를 캐시로 사용할 때 가장 일반적으로 배치하는 방법이다.
- 위의 그림과 같이 애플리케이션은 찾고자 하는 데이터가 먼저 캐시에 있는지를 확인한 뒤,
캐시에 데이터가 있으면 캐시에서 데이터를 읽어온다.
이를 캐시 히트라 한다.
- 찾고자 하는 데이터가 없을 때에는 캐시 미스가 발생하며,
이때에는 아래 그림과 같이 동작한다.
- 레디스로부터 데이터가 없다는 응답을 받은 애플리케이션은 직접 데이터베이스에 접근해
찾고자 하는 데이터를 가져온다. 그 뒤 애플리케이션은 이를 다시 캐시에 저장하는 과정을 거친다.
(2) look aside 구조의 장점
- look aside 구조의 장점은 레디스에 문제가 생겨 접근을 할 수 없는 상황이 발생하더라도
바로 서비스 장애로 이어지지 않고, 데이터베이스에서 데이터를 가지고 올 수 있다는 것이다.
- 하지만 기존에 애플리케이션에서 레디스를 통해 데이터를 가져오는 연결이 매우 많았다면
모든 커넥션이 한꺼번에 원본 데이터베이스로 몰려 많은 부하를 발생시키고,
이로 인해 원본 데이터베이스의 응답이 느려지거나 리소스를 많이 차지하는 등의 이슈가 발생해
애플리케이션의 성능에 영향을 미칠 수 있다.
(3) 캐시 워밍
- 따라서 이럴 때에는 미리 데이터베이스에서 캐시로 데이터를 밀어넣어주는 작업을 하기도 하는데,
이를 캐시 워밍(Cache warming)이라고도 한다.
아래는 이를 나타내는 그림이다.
- 공연 예매를 하는 애플리케이션에서 공연의 상세 정보를 저장하기 위해 레디스를 사용한다고 해보자.
새로운 공연 정보를 데이터베이스에만 등록한다면 공연이 오픈된 뒤 상품 정보를 레디스에서
먼저 찾아본 다음 데이터베이스에서 읽어와 레디스에 저장하는 캐시 미스 과정이 발생한다.
- 따라서 이럴 경우 상품이 오픈하기 전 데이터베이스에 저장된 데이터를 레디스로 밀어넣는
캐시 워밍 작업을 거치는 것이 효율적일 수 있다.
(4) 쓰기 전략과 캐시의 일관성
- 캐시는 데이터베이스에 저장돼 있는 데이터를 단순히 복사해 온 값이다.
따라서 원본 데이터와 동일한 값을 갖도록 유지하는 것이 필수적이다.
- 만약 데이터가 변경될 때 원본 데이터베이스에만 업데이트돼 캐시에는 변경된 값이 반영되지 않는다면
데이터 간 불일치가 일어난다. 이를 캐시 불일치(cache inconsitency)라 한다.
- 위의 그림 5-5에서 a라는 값은 데이터베이스 28로 업데이트됐지만,
레디스에는 아직 3인 데이터가 저장돼 있다.
이럴 경우 다른 애플리케이션이 a라는 값을 레디스에 먼저 찾아볼 경우 잘못된 값을 반환하는 상황이 발생할 수 있다.
- 캐시를 이용한 쓰기 전략은 대표적으로 세 가지가 있다. 하나씩 특징을 알아보자.
(4-1) Write through
- write through 방식은 데이터베이스에 업데이트할 때마다
매번 캐시에도 데이터를 함께 업데이트 시키는 방식으로,
위의 그림과 같다.
- 캐시는 항상 최신 데이터를 가지고 있을 수 있다는 장점이 있지만,
데이터는 매번 2개의 저장소에 저장돼야 하기 때문에 데이터를 쓸 때마다 시간이 많이 소요될 수 있다는
단점이 있다
(4-2) Cache invalidation
- 위의 그림에서처럼 cache invalidation은 데이터베이스에 값을 업데이트할 때마다
캐시에서는 데이터를 삭제하는 전략이다.
- 저장소에서 특정 데이터를 삭제하는 것이 새로운 데이터를 저장하는 것보다 훨씬
리소스를 적게 사용하기 때문에 앞선 write through의 단점을 보완한 방법이라고 볼 수 있다.
(4-3) write behind(write back)
- 만약 쓰기가 빈번하게 발생하는 서비스라면 위 그림의 write behind 방식을 고려해볼 수 있다.
데이터베이스에 대량의 쓰기 작업이 발생하면 이는 많은 디스크 I/O가 발생해, 성능 저하가 발생할 수 있다.
따라서 먼저 데이터를 빠르게 접근할 수 있는 캐시에 업데이트한 뒤,
이후에는 건수간 특정 시간 간격 등에 따라 비동기적으로 데이터베이스에 업데이트하는 것이다.
- 저장되는 데이터가 실시간으로 정확한 데이터가 아니어도 되는 경우 이 방법이 유용할 수 있다.
예를 들어, 유튜브와 같은 스트리밍 사이트의 동영상 좋아요 수는 매번 실시간 집계가 필요하진 않다.
누군가가 업데이트를 할 때마다 그 데이터가 바로 관계형 데이터베이스에 업데이트된다면
이는 심각한 성능 저하를 가져올 수 있다.
- 좋아요를 누른 데이터를 우선 레디스에 저장해둔 다음 5분 간격으로 이를 집계해
데이터베이스에 저장하는 과정을 거친다면 데이터베이스의 성능을 향상시켜
애플리케이션의 성능도 향상시킬 수 있다.
- 물론 이 방법에서는 캐시에 문제가 생겨 데이터가 날아갈 경우 최대 5분 동안의
데이터가 날아갈 수 있다는 위험성이 있음을 감수해야 한다
5) 캐시에서의 데이터 흐름
- 기본적으로 캐시는 데이터 스토어가 갖고 있는 데이터 중 사용자가 자주 사용할만한 데이터를 갖고 와서
임시로 저장하는 저장소다.
따라서 데이터 스토어보다 적은 양을 보관하는 데이터베이스의 서브셋이라고 볼 수 있다.
레디스는 특히 메모리에 모든 데이터를 저장하며, 기본적으로 메모리는 서버의 스토리지보다
훨씬 적은 양을 보관할 수 밖에 없다.
- 따라서 캐시는 항상 같이 가득 차지 않게 일정 양의 데이터를 유지해야 하며
계속해서 새로운 데이터가 저장되고 기존 데이터는 삭제될 수 있도록 관리돼야 한다.
- 따라서 캐시로 레디스를 사용할 때에는 데이터를 저장함과 동시에 적절한 시간의 TTL 값을
지정하는 것이 좋다.
6) 만료 시간
- 레디스에서 만료 시간, 즉 TTL은 데이터가 얼마나 오래 저장될 것인지를 나타내는 시간 설정이다.
레디스는 데이터베이스 내의 특정 키에 대한 만료 시간을 설정할 수 있으며,
이는 데이터의 유효 기간 또는 만료 시간을 정의하는 데 사용한다.
- 만료 시간은 일반적으로 초(second) 단위로 표현되며, 특정 키에 대한 만료 시간이 설정되면
해당 키와 관련된 데이터는 지정된 시간이 지난 후에 레디스에서 자동으로 삭제된다.
7) 만료 시간 설정을 위한 EXPIRE 커맨드
- 레디스에 저장된 키에 EXPIRE 커맨드를 사용하면 만료 시간을 설정할 수 있다.
SET 커맨드로 string 데이터를 저장할 때에는 EX 옵션을 함께 사용해 데이터의 저장과
동시에 만료 시간을 설정할 수도 있다.
- TTL 커맨드를 사용하면 키에 대한 만료 시간을 확인할 수 있는데,
만료 시간이 남아 있다면 남은 시간을 반환하며, 키가 존재하지 않을 때에는 -2를,
키에 대해 만료 시간이 지정되지 않았을 경우에는 -1을 반환한다.
'개발자를 위한 레디스' 카테고리의 다른 글
[개발자를 위한 레디스] 레디스의 자료 구조 - SET (0) | 2024.12.13 |
---|---|
[개발자를 위한 레디스] 레디스의 자료 구조 - hash (0) | 2024.12.13 |
[개발자를 위한 레디스] 레디스의 Pub/Sub (0) | 2024.12.12 |
[개발자를 위한 레디스] 레디스를 캐시로 사용하기 - 메모리 관리와 maxmemory-policy 설정 (0) | 2024.12.05 |
[개발자를 위한 레디스] 레디스를 메시지 브로커로 사용하기 (0) | 2024.08.25 |