CS/DB: Redis

hyeppy·2025년 11월 2일

CS

목록 보기
7/11
post-thumbnail

[10분 테코톡] 저문, 라온의 Cache & Redis


1. Redis

Redis는 Remote Dictionary Server의 약자로 메모리 기반의 NoSQL 데이터베이스다.

Key-value 구조로 데이터를 저장하는데, 이는 자바의 HashMap과 유사한 형태다. 가장 큰 특징은 데이터를 디스크가 아닌 메모리(RAM)에 저장한다는 점인데, 덕분에 디스크 기반 데이터베이스보다 훨씬 빠른 응답 속도를 제공한다.

실무에서는 주로 캐싱 용도로 많이 사용된다. 자주 조회되는 데이터를 Redis에 저장해 두면 매번 DB를 거치지 않아도 되므로 성능이 크게 향상된다. 또한 서버에서 공유해야 하는 세션 정보를 저장하거나, 게임이나 SNS의 실시간 랭킹 시스템을 구현할 때도 사용된다.


2. 왜 Redis인가?

2-1. 메모리의 속도

컴퓨터의 메모리 계층 구조를 보면 속도 차이가 명확함을 알 수 있는데,

CPU Register > Cache Memory > RAM(메모리) > SSD > HDD(디스크)

일반적인 RDBMS(MySQL, PostgreSQL 등)는 디스크에 데이터를 저장하는 반면, Redis는 RAM에 저장한다. 이 차이로 인해 Redis는 디스크에 저장하는 방식보다 수십 많게는 수백 배 빠른 응답 속도를 제공한다.

2-2. 다양한 자료구조 지원

Redis는 단순한 Key-Value 저장소가 아니라 여러 자료구조(Collection)를 제공한다

  1. String: 일반적인 Key-Value 형태
  2. List: LinkedList처럼 순서가 있는 데이터를 저장할 때 사용
    1. 채팅 메시지를 시간 순서대로 저장하는 경우
  3. Set: 중복을 허용하지 않는 집합
    1. 특정 게시물에 좋아요를 누른 사용자 목록 같은 데이터를 저장할 때 유용
  4. Sorted Set: Set에 점수 개념이 추가된 구조
    1. 실시간 랭킹 시스템에 적합
  5. Hash: 객체를 저장하는 구조
    1. 사용자 프로필처럼 여러 필드를 가진 데이터를 효율적으로 관리

2-3. Single Thread의 장점

Redis는 기본적으로 싱글 스레드로 동작하는데, 이로 인해 멀티 스레드 환경에서 발생하는 Race Condition(동시성 문제)를 원천적으로 차단하며, Context Switching 비용이 없어 효율적이다.

Race Condition이란?
여러 스레드가 동시에 같은 데이터를 수정하려고 할 때 발생하는 문제

예시
Thread 1: 조회수 100을 읽음 → 101로 증가 → 저장
Thread 2: 조회수 100을 읽음 → 101로 증가 → 저장
결과: 조회수가 102가 아닌 101이 됨 (잘못된 결과)

Redis는 싱글 스레드이므로 한 번에 하나의 명령만 처리하여 이런 문제가 발생하지 않는다.


3. 캐싱(Caching)이란?

3-1. 캐시

파레토 법칙을 이해하면 캐시가 주는 이점에 대해 이해하기 쉬운데, 캐시란 자주 사용되는 데이터를 미리 저장해 두는 임시 저장소를 뜻한다. 만약 인터넷 쇼핑몰에서 인기 상품 정보와 같은 자주 조회되는 데이터를 캐시에 저장해 두면 매번 값을 받으러 DB에 접근하지 않아도 되어 성능이 크게 향상된다.

3-2. Cache Hit vs Cache Miss

Cache Hit

요청한 데이터가 캐시에 존재하는 경우로, Redis에서 바로 데이터를 반환하므로 매우 빠르다.

Cache Miss

캐시에 데이터가 없어서 DB까지 조회해야 하는 경우다. 이때는 DB에서 데이터를 가져온 후 다음 요청을 위해 Redis에 저장해 둔다.

보통 캐시의 효율은 Cache Hit의 비율이 얼마나 높은지로 결정된다.


4. 캐싱 전략 패턴

4-1. 읽기 전략

  1. Look-Aside (Cache-Aside)

가장 일반적으로 사용되는 패턴이다. 애플리케이션이 데이터를 요청하면 먼저 Redis 캐시를 확인한다. 캐시에 데이터가 있으면 바로 반환하고(Cache Hit), 없으면 DB를 조회한다(Cache Miss). DB에서 조회한 데이터는 다음 요청을 위해 Redis에 저장한 후 반환한다.

장점

  • 캐시에 장애가 발생해도 DB 직접 조회가 가능해 서비스가 중단되지 않음
  • 실제로 요청된 데이터만 캐시에 저장되므로 메모리를 효율적으로 사용
  • 자주 조회되지만 변경이 적은 데이터에 적합

단점

  • 첫 조회 시에는 무조건 DB를 거쳐야 하므로 초기 부하가 발생할 수 있음
  • 캐시와 DB 간 데이터 정합성 문제를 고려해야 함
  1. Read-Through

캐시가 DB 조회를 대신 처리하는 패턴이다. 애플리케이션은 항상 캐시에만 요청하고, 캐시에 데이터가 없으면 캐시 계층이 직접 DB를 조회하여 데이터를 조회한 뒤 반환한다.

장점

  • 캐시와 DB 간 데이터 정합성이 보장되고 애플리케이션 코드가 단순해짐

단점

  • 캐시에 장애가 발생하면 서비스 전체가 중단될 위험 있음
  • 캐시 계층에 DB 조회 로직을 추가 구현해야 함

4-2. 쓰기 전략

  1. Write-Around

데이터 쓰기 시 DB에만 기록하고 캐시는 무효화하는 방식이다. 데이터가 변경되면 DB에 저장하고, Redis의 해당 키를 삭제한다. 다음 조회 시에는 새로운 데이터가 DB에서 조회되어 캐시에 저장된다

장점

  • 구현이 간단하고 성능이 좋으며 불필요한 캐시 쓰기 작업이 줄어듦

단점

  • 캐시 삭제와 DB 저장 사이에 다른 요청이 들어오면 일시적으로 캐시와 DB 데이터가 불일치할 수 있음
  1. Write-Through

데이터 쓰기 시 캐시와 DB에 동시에 기록하는 방식이다. 데이터가 변경되면 Redis와 DB 양쪽에 모두 기록하고, 두 곳 모두 성공해야 쓰기 작업이 완료된다.

장점

  • 캐시와 DB의 데이터 정합성을 완벽하게 보장하고 데이터 유실 위험이 최소화됨

단점

  • 두 번의 쓰기 작업이 필요하므로 성능이 저하되고 응답 시간이 길어짐
  1. Write-Back (Write-Behind)

캐시에만 먼저 쓰고, 나중에 일괄적으로 DB에 반영하는 방식이다. 데이터 변경 시 Redis에만 기록하고, 일정 시간이 지나거나 일정 개수가 쌓이면 DB에 한꺼번에 저장한다.

장점

  • 쓰기 성능이 매우 빠르고 DB 쓰기 부하가 크게 감소
  • 로그 수집이나 조회수 카운팅처럼 유실되어도 크게 문제되지 않는 데이터에 적합

단점

  • 캐시에 장애가 발생하면 아직 DB에 반영되지 않은 데이터 유실 가능성
  • 구현 복잡도가 높음

5. Redis 영속성 (Persistence)

Redis는 메모리 기반이므로 서버가 재시작되면 데이터가 사라진다. 이를 방지하기 위한 영속성 기능으로 RDB와 AOF 두 가지 방식을 제공한다.

5-1. RDB (Redis Database)

특정 시점의 메모리 스냅샷을 디스크에 저장하는 방식이다. 예를 들어 5분마다 또는 1000개 이상 변경 시 자동으로 스냅샷을 생성하도록 설정할 수 있다. 스냅샷은 dump.rdb 파일로 저장되며, 서버 재시작 시 이 파일을 로드하여 데이터를 복구한다.

장점

  • 파일 크기가 작고 복구 속도가 빠르며 성능 영향이 적음
  • 일부 데이터 유실이 허용되거나 빠른 복구가 중요한 경우에 적합

단점

  • 스냅샷을 찍는 시점 사이에 발생한 데이터는 유실될 수 있음
  • 데용량 데이터의 경우 스냅샷 생성 시 부하가 발생할 수 있음

5-2. AOF (Append Only File)

모든 쓰기 명령어를 로그 파일에 기록하는 방식이다. SET, INSERT, DELETE 등의 명령어가 실행될 때마다 파일에 기록되며, 서버 재시작 시 이 파일을 순차적으로 재생하여 데이터를 복원한다.

장점

  • 데이터 유실이 거의 없음 (거의 실시간 백업)
  • 사람이 읽을 수 있는 형식
  • 파일 손상 시에도 일부 복구가 가능
  • 결제 정보나 중요한 사용자 데이터처럼 데이터 유실이 절대 안 되는 경우에 적합

단점

  • 모든 명령어를 기록하므로 파일 크기가 매우 크고, 복구 시간이 오래 걸림
  • 모든 쓰기마다 디스크에 접근하므로 성능 영향이 있음

5-3. RDB + AOF

RDB로 주기적인 백업을 수행하여 빠른 복구를 가능하게 하고, AOF로 실시간 로깅을 하여 데이터 유실을 방지하는 방식이 많이 사용되고 있다. 복구 시에는 더 최신 데이터를 담고 있는 AOF를 우선으로 사용한다.


7. Redis 사용 시 주의사항

7-1. O(N) 명령어 주의

Redis는 싱글 스레드이므로 시간 복잡도가 높은 명령어는 전체 서비스를 느리게 만든다. 특히 KEYS * 명령어는 모든 키를 조회하는데, 운영 환경에서는 절대 사용하면 안 된다. FLUSHALL이나 FLUSHDB는 모든 데이터를 삭제하므로 신중해야 하고, HGETALL이나 SMEMBERS처럼 Hash나 Set의 모든 요소를 한 번에 조회하는 명령어도 데이터 크기가 크면 위험하다.

7-2. 메모리 관리

데이터를 저장하고 삭제하는 과정에서 메모리 파편화가 발생할 수 있다. 이는 메모리 공간이 조각 나서 실제로는 여유가 있어도 사용하지 못하는 상황이 생기는 것이다. 이를 방지하기 위해 maxmemory 설정으로 최대 메모리를 제한하고, maxmemory-policy로 메모리 부족 시 어떻게 처리할지 정책을 지정해야 한다.

7-3. 캐시 사용 원칙

캐시에 적합한 데이터는 자주 조회되고, 변경이 적으며, 유실되어도 큰 문제가 없는 데이터다. 반대로 변경이 잦거나, 정합성이 중요하거나(결제 정보 등), 조회 빈도가 낮은 데이터는 캐시에 적합하지 않다.

7-4. 데이터 정합성

캐시와 DB 간 데이터 불일치를 최소화하는 방법은 세 가지가 있다.

  1. TTL을 짧게 설정하여 자주 갱신되도록 한다.
  2. 데이터 변경 시 캐시를 삭제하여 다음 조회 시 최신 데이터가 반영되도록 한다.
  3. 서버 시작 시 자주 사용되는 데이터를 미리 캐싱하는 Cache Warming을 수행한다.

7-5. Redis 용도 명확히 하기

Redis를 사용할 때는 목적을 명확히 해야 한다. 캐시로 사용할 것인지, 데이터 저장소로 사용할 것인지에 따라 설정과 운영 방식이 완전히 달라질 수 있다.

  1. 캐시용으로 사용할 때

캐시는 성능 향상을 위한 저장소이므로 데이터 유실이 허용된다. 따라서 빠른 응답 속도를 최우선으로 하며 TTL을 적극 활용하면 좋다. 영속성 설정은 불필요하거나 최소한으로 구성한다.

예를 들어 상품 목록이나 사용자 프로필 같은 데이터는 캐시에서 사라져도 DB에서 다시 가져올 수 있으므로 문제가 없다.

  1. 저장소용으로 사용할 때

저장소는 데이터의 영구적인 보관이 목적이므로 데이터 유실이 절대 허용되지 않는다. 따라서 RDB와 AOF를 모두 활성화하여 영속성을 보장하고, Replication이나 Sentinel을 구성하여 고가용성을 확보해야 한다. 실시간 랭킹 데이터나 장바구니 정보처럼 유실되지 않아야 하는 데이터가 이에 해당한다.

용도를 구분하여 사용할 때는 가능한 캐시 전용 Redis와 인스턴스와 저장소 전용 Redis를 구분해서 운영하는 것이 구현 측면과 관리 측면에서 더 유용할 것이다.


profile
Backend

0개의 댓글