Redis의 기본 개념과 활용

Redis 개요 및 핵심 특징

Redis는 높은 성능의 분산 키-값 저장 시스템으로, 캐시와 분산 데이터 구조 서버로 작동합니다. 이를 통해 고컨커런시 환경에서 데이터 접근 속도를 향상시키고 다양한 비즈니스 요구 사항을 처리할 수 있습니다.

1. Redis의 주요 기능

  • 캐시: 데이터베이스 병목 현상을 해결하고 읽기 성능을 향상시킵니다.
  • 분산 데이터 구조 서버: String, Hash, List, Set, Sorted Set 등 다양한 데이터 구조를 지원하여 카운터, 순위표, 분산 세션 등을 구현합니다.
  • 경량 메시지 큐: Pub/Sub 또는 Streams를 사용해 비동기 통신을 제공합니다.

2. Redis의 주요 특성

특성 설명
싱글 스레드 모델 모든 요청을 하나의 스레드에서 처리하며 epoll 다중 플렉싱을 통해 10만 QPS 이상을 지원합니다.
키-값 저장소 키는 문자열이며 값은 다양한 데이터 구조를 지원합니다.
만료 메커니즘 TTL(Time To Live) 기반으로 키가 자동으로 만료됩니다.
지속화 RDB(스냅샷)와 AOF(로그)를 통해 데이터 안전성을 보장합니다.
고가용성 마스터-슬레이브 복제, 센티널(Sentinel), Redis 클러스터를 통해 단일 포인트 오류를 해결합니다.

Redis의 성능 요인

1. 싱글 스레드 모델의 원리

Redis의 싱글 스레드는 실제로 모든 요청을 하나의 스레드에서 처리하지만, IO 작업이나 지속화 작업은 별도의 스레드에서 실행됩니다. 이 덕분에:

  • 문맥 전환이 필요 없어 효율적입니다.
  • epoll 다중 플렉싱을 통해 여러 클라이언트 연결을 동시에 처리합니다.
  • C 언어로 구현된 최적화된 데이터 구조를 사용합니다.

2. 데이터 구조의 구현

다음 표는 각 데이터 구조의 구현 방식과 주요 특징을 보여줍니다.

데이터 구조 구현 방법 핵심 특징 적용 예시
스트링(String) SDS(Simple Dynamic String) 가변 길이 문자열, 예비 할당을 지원합니다. 캐시, 카운터, 분산 잠금
해시(Hash) 압축 리스트(ziplist) → 해시 테이블 작은 데이터에는 압축 리스트를 사용해 메모리를 절약합니다. 객체 속성 저장, 장바구니 관리
리스트(List) 빠른 리스트(quicklist) 양방향 연결 리스트와 압축 리스트를 결합하여 메모리 사용을 줄입니다. 메시지 큐, 최근 활동 목록
셋(Set) 정수 집합(intset) → 해시 테이블 작은 정수 집합에서는 메모리 절약 효과를 제공합니다. 중복 제거, 공통 친구 찾기
정렬된 셋(Sorted Set) 스킵 리스트(skiplist) + 해시 테이블 O(logN) 범위 쿼리를 지원합니다. 랭킹 시스템, 시간대별 정렬

3. 지속화 메커니즘

Redis의 지속화는 RDB와 AOF를 조합하여 데이터 손실 문제를 해결합니다.

(1) RDB(Redis Database Snapshot)

  • 주기적으로 메모리 데이터를 디스크에 스냅샷으로 저장합니다.
  • `save`, `bgsave` 명령어로 수동 또는 자동 트리거됩니다.
  • 파일 크기가 작고 복구 속도가 빠릅니다.

(2) AOF(Append Only File)

  • 모든 쓰기 작업을 텍스트 형태로 파일에 추가합니다.
  • `appendfsync always`, `everysec`, `no` 옵션으로 동기화 정책을 설정할 수 있습니다.
  • AOF 재작성(AOF Rewrite)을 통해 로그를 최적화합니다.

(3) 혼합 지속화(Mixed Persistence)

  • RDB 스냅샷과 AOF 로그를 결합한 방식으로, 성능과 안전성을 모두 확보합니다.

고급 특징 및 활용

1. 분산 잠금

Redis를 사용한 분산 잠금은 여러 프로세스 간의 상호 배제를 보장합니다.


-- Lua 스크립트로 잠금 해제
if redis.call("get", KEYS[1]) == ARGV[1] then
    return redis.call("del", KEYS[1])
else
    return 0
end

2. 트랜잭션 및 낙관적 잠금

Redis의 트랜잭션은 MULTI/EXEC 명령어를 통해 수행되며, WATCH 메커니즘을 통해 낙관적 잠금을 구현할 수 있습니다.

3. 스트림(Streams)

Redis 5.0부터 제공되는 스트림은 메시지를 영구적으로 저장하고 소비자 그룹을 관리할 수 있는 기능을 제공합니다.

실제 적용 사례

1. 캐시

데이터베이스로부터 가져온 데이터를 Redis에 저장하고 일정 시간 이후 자동 삭제합니다.

2. 카운터


# Python 코드로 인크먼트 구현
redis.incr('like_count:article_123')

3. 순위표


# Python 코드로 순위표 생성
redis.zadd('rank', {'user_1': 1000, 'user_2': 900})
top_users = redis.zrevrange('rank', 0, 9, withscores=True)

4. 분산 세션

Spring Boot 구성 예시:


spring:
  session:
    store-type: redis
    timeout: 1800 # 초 단위

5. 리밋팅


public boolean limitRequest(String ip) {
    long now = System.currentTimeMillis();
    long window = 1000; // 1초
    long maxRequests = 100;
    redis.zremrangeByScore(ip, "0", String.valueOf(now - window));
    long count = redis.zcard(ip);
    if (count < maxRequests) {
        redis.zadd(ip, now, String.valueOf(now));
        redis.expire(ip, (int)(window / 1000));
        return true;
    }
    return false;
}

인터뷰 대비 질문

  • Redis의 스레드 모델은 무엇이며 왜 싱글 스레드인지?
  • RDB와 AOF의 차이는 무엇이며 어떻게 사용해야 하나?
  • 캐시 페너스의 해결 방법은?
  • 분산 잠금의 신뢰성은 어떻게 보장하나?

태그: Redis DataStructures HighAvailability

7월 21일 18:32에 게시됨