[10분 테코톡] 저문, 라온의 Cache & Redis
Redis는 Remote Dictionary Server의 약자로 메모리 기반의 NoSQL 데이터베이스다.
Key-value 구조로 데이터를 저장하는데, 이는 자바의 HashMap과 유사한 형태다. 가장 큰 특징은 데이터를 디스크가 아닌 메모리(RAM)에 저장한다는 점인데, 덕분에 디스크 기반 데이터베이스보다 훨씬 빠른 응답 속도를 제공한다.
실무에서는 주로 캐싱 용도로 많이 사용된다. 자주 조회되는 데이터를 Redis에 저장해 두면 매번 DB를 거치지 않아도 되므로 성능이 크게 향상된다. 또한 서버에서 공유해야 하는 세션 정보를 저장하거나, 게임이나 SNS의 실시간 랭킹 시스템을 구현할 때도 사용된다.
컴퓨터의 메모리 계층 구조를 보면 속도 차이가 명확함을 알 수 있는데,
CPU Register > Cache Memory > RAM(메모리) > SSD > HDD(디스크)
일반적인 RDBMS(MySQL, PostgreSQL 등)는 디스크에 데이터를 저장하는 반면, Redis는 RAM에 저장한다. 이 차이로 인해 Redis는 디스크에 저장하는 방식보다 수십 많게는 수백 배 빠른 응답 속도를 제공한다.
Redis는 단순한 Key-Value 저장소가 아니라 여러 자료구조(Collection)를 제공한다
Redis는 기본적으로 싱글 스레드로 동작하는데, 이로 인해 멀티 스레드 환경에서 발생하는 Race Condition(동시성 문제)를 원천적으로 차단하며, Context Switching 비용이 없어 효율적이다.
Race Condition이란?
여러 스레드가 동시에 같은 데이터를 수정하려고 할 때 발생하는 문제
예시
Thread 1: 조회수 100을 읽음 → 101로 증가 → 저장
Thread 2: 조회수 100을 읽음 → 101로 증가 → 저장
결과: 조회수가 102가 아닌 101이 됨 (잘못된 결과)
Redis는 싱글 스레드이므로 한 번에 하나의 명령만 처리하여 이런 문제가 발생하지 않는다.
파레토 법칙을 이해하면 캐시가 주는 이점에 대해 이해하기 쉬운데, 캐시란 자주 사용되는 데이터를 미리 저장해 두는 임시 저장소를 뜻한다. 만약 인터넷 쇼핑몰에서 인기 상품 정보와 같은 자주 조회되는 데이터를 캐시에 저장해 두면 매번 값을 받으러 DB에 접근하지 않아도 되어 성능이 크게 향상된다.
Cache Hit
요청한 데이터가 캐시에 존재하는 경우로, Redis에서 바로 데이터를 반환하므로 매우 빠르다.
Cache Miss
캐시에 데이터가 없어서 DB까지 조회해야 하는 경우다. 이때는 DB에서 데이터를 가져온 후 다음 요청을 위해 Redis에 저장해 둔다.
보통 캐시의 효율은 Cache Hit의 비율이 얼마나 높은지로 결정된다.
가장 일반적으로 사용되는 패턴이다. 애플리케이션이 데이터를 요청하면 먼저 Redis 캐시를 확인한다. 캐시에 데이터가 있으면 바로 반환하고(Cache Hit), 없으면 DB를 조회한다(Cache Miss). DB에서 조회한 데이터는 다음 요청을 위해 Redis에 저장한 후 반환한다.
장점
단점
캐시가 DB 조회를 대신 처리하는 패턴이다. 애플리케이션은 항상 캐시에만 요청하고, 캐시에 데이터가 없으면 캐시 계층이 직접 DB를 조회하여 데이터를 조회한 뒤 반환한다.
장점
단점
데이터 쓰기 시 DB에만 기록하고 캐시는 무효화하는 방식이다. 데이터가 변경되면 DB에 저장하고, Redis의 해당 키를 삭제한다. 다음 조회 시에는 새로운 데이터가 DB에서 조회되어 캐시에 저장된다
장점
단점
데이터 쓰기 시 캐시와 DB에 동시에 기록하는 방식이다. 데이터가 변경되면 Redis와 DB 양쪽에 모두 기록하고, 두 곳 모두 성공해야 쓰기 작업이 완료된다.
장점
단점
캐시에만 먼저 쓰고, 나중에 일괄적으로 DB에 반영하는 방식이다. 데이터 변경 시 Redis에만 기록하고, 일정 시간이 지나거나 일정 개수가 쌓이면 DB에 한꺼번에 저장한다.
장점
단점
Redis는 메모리 기반이므로 서버가 재시작되면 데이터가 사라진다. 이를 방지하기 위한 영속성 기능으로 RDB와 AOF 두 가지 방식을 제공한다.
특정 시점의 메모리 스냅샷을 디스크에 저장하는 방식이다. 예를 들어 5분마다 또는 1000개 이상 변경 시 자동으로 스냅샷을 생성하도록 설정할 수 있다. 스냅샷은 dump.rdb 파일로 저장되며, 서버 재시작 시 이 파일을 로드하여 데이터를 복구한다.
장점
단점
모든 쓰기 명령어를 로그 파일에 기록하는 방식이다. SET, INSERT, DELETE 등의 명령어가 실행될 때마다 파일에 기록되며, 서버 재시작 시 이 파일을 순차적으로 재생하여 데이터를 복원한다.
장점
단점
RDB로 주기적인 백업을 수행하여 빠른 복구를 가능하게 하고, AOF로 실시간 로깅을 하여 데이터 유실을 방지하는 방식이 많이 사용되고 있다. 복구 시에는 더 최신 데이터를 담고 있는 AOF를 우선으로 사용한다.
Redis는 싱글 스레드이므로 시간 복잡도가 높은 명령어는 전체 서비스를 느리게 만든다. 특히 KEYS * 명령어는 모든 키를 조회하는데, 운영 환경에서는 절대 사용하면 안 된다. FLUSHALL이나 FLUSHDB는 모든 데이터를 삭제하므로 신중해야 하고, HGETALL이나 SMEMBERS처럼 Hash나 Set의 모든 요소를 한 번에 조회하는 명령어도 데이터 크기가 크면 위험하다.
데이터를 저장하고 삭제하는 과정에서 메모리 파편화가 발생할 수 있다. 이는 메모리 공간이 조각 나서 실제로는 여유가 있어도 사용하지 못하는 상황이 생기는 것이다. 이를 방지하기 위해 maxmemory 설정으로 최대 메모리를 제한하고, maxmemory-policy로 메모리 부족 시 어떻게 처리할지 정책을 지정해야 한다.
캐시에 적합한 데이터는 자주 조회되고, 변경이 적으며, 유실되어도 큰 문제가 없는 데이터다. 반대로 변경이 잦거나, 정합성이 중요하거나(결제 정보 등), 조회 빈도가 낮은 데이터는 캐시에 적합하지 않다.
캐시와 DB 간 데이터 불일치를 최소화하는 방법은 세 가지가 있다.
Redis를 사용할 때는 목적을 명확히 해야 한다. 캐시로 사용할 것인지, 데이터 저장소로 사용할 것인지에 따라 설정과 운영 방식이 완전히 달라질 수 있다.
캐시는 성능 향상을 위한 저장소이므로 데이터 유실이 허용된다. 따라서 빠른 응답 속도를 최우선으로 하며 TTL을 적극 활용하면 좋다. 영속성 설정은 불필요하거나 최소한으로 구성한다.
예를 들어 상품 목록이나 사용자 프로필 같은 데이터는 캐시에서 사라져도 DB에서 다시 가져올 수 있으므로 문제가 없다.
저장소는 데이터의 영구적인 보관이 목적이므로 데이터 유실이 절대 허용되지 않는다. 따라서 RDB와 AOF를 모두 활성화하여 영속성을 보장하고, Replication이나 Sentinel을 구성하여 고가용성을 확보해야 한다. 실시간 랭킹 데이터나 장바구니 정보처럼 유실되지 않아야 하는 데이터가 이에 해당한다.
용도를 구분하여 사용할 때는 가능한 캐시 전용 Redis와 인스턴스와 저장소 전용 Redis를 구분해서 운영하는 것이 구현 측면과 관리 측면에서 더 유용할 것이다.