Cache
Cache의 개념
Cache란 나중에 요청할 결과를 미리 저장해둔 후 빠르게 서비스해 주는 것을 의미합니다. 즉, 미리 결과를 저장하고 나중에 요청이 오면 그 요청에 대해서 DB 또는 API를 참조하지 않고 Cache에 접근하여 요청을 처리하는 기법입니다.
이러한 cache가 나온 배경에는 파레토 법칙이 있습니다.
파레토 법칙이란 80%의 결과는 20%의 원인으로 인해 발생한다는 뜻입니다. 즉, 캐시는 모든 결과를 캐싱할 필요가 없으며 서비스를 할 때 많이 사용되는 20%만 캐싱함으로써 전체적으로 효율을 끌어올릴 수 있다는 것입니다.
Cache를 언제 사용하게 되는가?
서비스를 처음 운영할 때는 WEB - WAS - DB 정도로 작게 인프라를 구축하는데, 사용자가 늘어나면 DB에 무리가 가기 시작합니다.
DB는 데이터를 물리 디스크에 직접 쓰기 때문에 서버에 문제가 발생해도 데이터가 손실되지는 않지만, 매 트랜잭션마다 디스크에 접근해야하므로 부하가 많아지면 성능이 떨어집니다.
그래서 사용자가 늘어나면 DB를 스케일 인 또는 스케일 아웃하는 방식 외에도 캐시 서버를 검토하게 됩니다.
Cache 동작 방식
클라이언트가 웹 서버에 요청을 보내면, 웹 서버는 데이터를 DB에서 가져 오기 전에 캐시에 데이터가 있는지 확인하고, 있다면 바로 클라이언트에게 저장된 데이터를 반환합니다. 이를 Cache Hit라고 합니다.
반대로 캐시 서버에 데이터가 없으면 DB에 데이터를 요청하여 원하는 데이터를 조회한 후 그 데이터를 클라이언트에게 제공하는데, 이를 Cache Miss라고 합니다.
위 flow에서 캐시를 어떻게 사용하느냐에 따라서 look aside cache와 write back으로 나뉩니다.
Look Aside Cache (Lazy Loading)
- 캐시에 데이터 존재 유무 확인
- 데이터가 있다면 캐시의 데이터 사용
- 데이터가 없다면 캐시의 실제 DB 데이터 사용
- DB에서 가져 온 데이터를 캐시에 저장
look aside cache는 캐시를 한 번 접근하여 데이터가 있는지 판단한 후, 있다면 캐시의 데이터를 사용하고 없으면 실제 DB 또는 API를 호출합니다. 대부분의 캐시를 사용한 개발이 해당 프로세스를 따릅니다.
Write Back
- 모든 데이터를 캐시에 저장
- 캐시의 데이터를 일정 주기마다 DB에 한꺼번에 저장 (배치)
- DB에 저장한 데이터를 캐시에서 제거
write back은 주로 쓰기 작업이 굉장히 많아서, INSERT 쿼리를 일일이 날리지 않고 한꺼번에 배치 처리를 하기 위해 사용합니다.
예를 들어 연휴나 금요일 저녁 같은 시간 대에 배달 서비스를 이용한다고 생각하면, 여러 고객이 동시에 주문 버튼을 누르면서 DB에 갑작스럽게 엄청난 주문 요청이 몰리게 되면 DB 서버가 죽을 수도 있습니다. 이때 write back 기반의 캐시를 사용하면 캐시 메모리에 데이터를 저장해 놓고, 이후 DB 디스크에 업데이트 해 주면 안전하게 쓰기 작업을 이행할 수 있는 것입니다.
DB에서 디스크를 접근하는 횟수가 줄어들기 때문에 성능 향상을 기대할 수 있지만, DB에 데이터를 저장하기 전에 캐시 서버가 죽으면 데이터가 유실된다는 문제점이 있습니다.
Redis
Redis란?
Redis는 오픈 소스로서 NoSQL로 분류되기도 하고, Memcached와 같이 인 메모리 솔루션으로 분류되기도 합니다. 성능은 Memcached에 버금가면서 다양한 데이터 구조체를 지원함으로써 DB, Cache, Message Queue, Shared Memory 용도로 사용될 수 있습니다.
한편, Redis는 Remote Dictionary Server의 약자로 외부에서 사용 가능한 Key-Value 쌍의 해시 맵 형태의 서버라고 생각할 수 있습니다. 그래서 별도의 쿼리 없이 Key를 통해 빠르게 결과를 가져올 수 있습니다.
또한, 디스크에 데이터를 쓰는 구조가 아니라 메모리에서 데이터를 처리하기 때문에 작업 속도가 상당히 빠릅니다.
이러한 Redis를 한 줄로 정의하자면, 고성능 키-값 저장소로서 String, list, hash, set, sorted set 등의 자료 구조를 지원하는 NoSQL 라고 할 수 있습니다.
Redis의 특징
이러한 레디스의 특징은 다음과 같습니다.
- 영속성을 지원하는 인 메모리 데이터 저장소
- 다양한 자료 구조를 지원
- 싱글 스레드 방식으로 인해 연산을 원자적으로 수행이 가능
- 읽기 성능 증대를 위한 서버 측 리플리케이션을 지원
- 쓰기 성능 증대를 위한 클라이언트 측 샤딩 지원
- 다양한 서비스에서 사용되며 검증된 기술
Redis의 영속성
Redis는 영속성을 보장하기 위해 데이터를 디스크에 저장할 수 있습니다. 서버가 내려가더라도 디스크에 저장된 데이터를 읽어서 메모리에 로딩합니다. 데이터를 디스크에 저장하는 방식은 크게 두 가지가 있습니다.
- RDB(Snapshotting) 방식
- 순간적으로 메모리에 있는 내용 전체를 디스크에 옮겨 담는 방식
- AOF(Append On File) 방식
- Redis의 모든 write/update 연산 자체를 모두 log 파일에 기록하는 형태
Redis의 컬렉션
개념만 봤을 때 Redis는 단순히 데이터를 메모리에 저장하여 빠른 DB입니다.
하지만 memchached와 달리 다양한 데이터 구조체를 지원합니다. 이를 컬렉션이라고 부르는데 다음 그림을 보겠습니다.

Key가 될 수 있는 데이터 구조체가 다양한 것을 알 수 있습니다. 이렇게 다양한 자료 구조를 지원하게 되면 개발의 편의성이 좋아지고 난이도가 낮아진다는 장점이 있습니다.
싱글 스레드를 사용하는 Redis
Redis는 싱글 스레드를 사용하므로 연산을 원자적으로 처리하여 Race Condition이 거의 발생하지 않습니다.
예를 들어, 친구 리스트의 친구를 추가하는 연산을 시도해 보겠습니다. 정상적인 상황에서는 유저 각각의 트랜잭션이 순서대로 잘 행해지고 있으므로 문제가 없습니다.
그러나 동시에 친구 리스트에 B, C를 추가한다고 하면 어떻게 될까요?
두 트랜잭션이 동일한 최종 상태인 A를 자신의 메모리로 읽어 들이고, 그 상태에서 각자 B 또는 C를 추가하게 되면 최종 상태가 (A, B) 혹은 (A, C)가 됩니다. (A, B) 혹은 (A, C)라고 한 이유는 컨텍스트 스위칭에 따라 두 트랜잭션 중 누가 먼저 끝날 지 예측할 수 없기 때문입니다.
물론 이러한 Race Condition을 해결하기 위해 격리 수준 등 여러 가지 기법이 있지만, Redis는 싱글 스레드를 사용해 하나의 트랜잭션은 하나의 명령만 실행할 수 있으므로 '다수'의 Race Condition을 해결할 수 있습니다.
('모든'이 아니라 '다수'인 이유는 더블 클릭 이슈는 싱글 스레드만으로 해결할 수 없기 때문입니다.)
'Development' 카테고리의 다른 글
| [JPA] Spring Data JPA는 어떻게 새로운 Entity인지 알아내는걸까? (0) | 2025.01.22 |
|---|---|
| [Server] RAID 란? (0) | 2025.01.20 |
| [ORM] OSIV 옵션이 무엇인가요 (2) | 2025.01.20 |
| [Network] 그런 REST API로 괜찮은가? 진짜? (0) | 2023.01.10 |
| [Network] REST API와 WebSocket API의 차이는? (1) | 2022.12.28 |
댓글