Redis 캐시로 사용하기
What is Caching?
Temporary Location For Speed
- 데이터의 원래 소스보다 더 빠르고 효율적으로 액세스할 수 있는 임시 데이터 저장소
`캐시`란 사용자의 입장에서 데이터의 원래 소스보다 빠르게 효율적으로 액세스 할 수 있는 임시 저장소를 말한다.
대부분의 애플리케이션에서 속도 향상을 위해 `캐시`를 사용한다.
일단 이 `캐시`를 효율적으로 접근하기 위한 조건은 다음과 같다.
- 원본 보다 빠른 접근 속도
- 같은 데이터 반복적 액세스(즉, 데이터 액세스 횟수가 1번 이상이여야 의미가 있음)
- 변하지 않는 데이터
Redis as a cache
Most popular software caching solution
- 단순한 key-value 구조
- In-memory 데이터 저장소(RAM)
- 빠른 성능
- 평균 작업속도 < 1ms
- 초당 수백만 건의 작업 가능
캐싱 전략(Caching Strategies)
Redis를 캐시로 사용할 때 어떻게 배치하느냐에 따라 시스템 성능에 큰 영향을 미치기도 한다.
이를 `캐싱 전략`이라고 하며 `캐싱 전략`은 데이터의 유형과 해당 데이터의 액세스 패턴을 잘 고려하여 선택해야 한다.
Look-Aside(Lazy Loading) - 읽기 전략
애플리케이션에서 데이터를 읽는 작업이 많을 때 사용하는 방식이 `Look-Aside` 방식이다.
위 방법은 `Redis`를 캐시로 사용할 때 가장 일반적으로 쓰는 방식이다.
애플리케이션은 데이터를 찾을 때 캐시를 먼저 확인한다.
만약, 캐시에 데이터가 있다면 캐시에서 데이터를 가져오는 작업을 반복한다.
만약, 찾는 키가 없으면 애플리케이션은 DB에 접근하여 데이터를 가져온 뒤 다시 `Redis`에 저장해야 한다.
따라서, `캐시`는 찾는 데이터가 없을 때만 입력되기 때문에 이를 `Lazy Loading`이라고도 부른다.
이 구조는 `Redis`가 다운되더라도 바로 장애로 이어지지 않고 DB에서 데이터를 가져올 수 있다.
하지만, 캐시로 붙어있던 커넥션이 많았다면 해당 커넥션이 전부 DB로 붙기 때문에
DB에 갑자기 많은 부하가 가해질 수 있다.
그래서 이런 경우에 `캐시`를 새로 투입하거나 DB에만 새로운 데이터를 저장했다면
처음엔 `Cache Miss`가 엄청 발생해서 성능 저하가 올 수 있다.
그래서 이런 경우에는 아래와 같은 `Cache Warming` 방식을 사용할 수 있다.
Cache Warming
미리 DB에서 캐시로 데이터를 밀어 넣어주는 작업을 `Cache Warming`이라고 한다.
실제로, 티켓 사이트 같은 경우 상품 오픈 전 상품의 정보를 DB에서 캐시로 올려주는 작업을 한다고 한다.
여기까지 읽기 전략의 `Look-Aside` 방식에 관해 알아보았다.
다음은 쓰기 전략의 캐싱 전략이다.
쓰기 전략
Write-Around 방식
- DB에만 데이터를 저장한다.
- 일단 모든 데이터는 DB에 저장한다.
- Cache Miss가 발생한 경우 `캐시`에 데이터를 끌어온다.
- 이 경우에는 캐시 내 데이터와 DB 내 데이터가 다를 수 있다.
Write-Through 방식
- DB에 데이터를 저장할 때 `캐시`에도 함께 데이터를 저장하는 방법이다.
- `캐시`는 항상 최신 정보를 갖고 있다는 장점이 있지만, 두 단계 스탭을 거쳐야 하므로 상대적으로 느리다고 볼 수 있다.
- 그리고 저장된 데이터가 재사용되지 않을 수 있는데 무조건 `캐시`에 넣어버리기 때문에 리소스 낭비일 수 있다.
- 따라서, 데이터를 저장할 때는 몇 분 혹은 몇 시간 동안만 데이터를 보관하겠다는 의미인 `ExpireTime`을 설정해주는 것이 좋다.
- 그런데 이 값을 어떻게 관리하는지가 장애 포인트가 될 수 있다.
사용하면 안되는 커맨드
`Redis`는 `Single Thread`로 동작한다.
한 사용자가 오래 걸리는 커맨드를 실행한다면 나머지 모든 요청들은 수행할 수 없고 대기하게 된다.
이로 인해 장애도 빈번하게 발생한다.
- `keys *` -> `scan`으로 대체
- 운영 환경에서 `keys *`를 사용하면 굉장하게 오래걸림
- scan을 사용하면 재귀적으로 key들을 호출할 수 있다.
- Hash나 Sorted Set 등 자료구조
- 키 나누기(최대 100만개)
- hgetall -> hscan
- del -> unlink
- key에 많은 데이터가 들어있을 때 `del`로 데이터를 지우면 그 키를 지우는 동안 아무것도 할 수 없다.
- 이때, `unlink`를 사용하면 키를 백그라운드로 지워주기에 권장된다.
변경하면 장애를 막을 수 있는 기본 설정값
volatile-lru
- LRU 정책 가장 최근에 사용하지 않은 키부터 삭제한다.
- 이때, Expire 설정이 있는 키값만 삭제
- 만약, 메모리에 Expire 설정값이 없는 키만 남아있다면 메모리가 가득찼을 때 에러 상황이 발생할 수 있다.
allkeys-lru
- 모든 key에 대해 LRU 정책을 사용하는 것이다.
- 따라서, 메모리가 가득찼을 때에 의한 에러 상황이 발생하지 않는다.
Cache Stampede
대규모 트래픽 환경에서 `TTL` 값을 너무 작게 설정할 경우 `Cache Stampede` 현상이 존재할 가능성이 존재한다.
`Look Aside` 패턴에서 `Redis`에 데이터가 없다는 응답을 받은 서버가 DB로 데이터를 요청한 뒤 이를 다시 `Redis`에 저장한다.
그런데 `Key`가 만료되는 순간 많은 서버에서 이 `Key`를 같이 보고 있었다면,
모든 애플리케이션 서버들이 DB에가서 같은 데이터를 찾게 되는 `Duplicate Read`가 발생한다.
또, 읽어 온 값들을 `Redis`에 각각 Write하는 `Duplicate Write`도 발생한다.
이는 굉장히 비효율적인 상황이며 한번 이런 상황이 발생하면 처리량도 느려지고 불필요한 작업들이 굉장히 늘어난다.
예를 들어, 티켓 사이트에서 인기 있는 공연이 오픈되면
하나의 공연 데이터를 읽기 위해 수십개의 애플리케이션 서버에서 커넥션이 연결된다.
따라서, 너무 작게 TTL 값을 설정하면 문제가 발생할 수 있다.
- TTL(Time-to-Live): 캐시 항목이 유효한 시간을 지정한다. 캐시 항목이 생성된 후, TTL이 지나면 해당 항목은 만료되어 삭제
참고
https://www.youtube.com/watch?v=92NizoBL4uA
'DB > Redis' 카테고리의 다른 글
[Redis] 레디스는 무엇일까? (0) | 2024.09.20 |
---|