Redis(Remote Dictionary Server)

아투·2026년 3월 27일

Database

목록 보기
3/6
post-thumbnail

Redis(Remote Dictionary Server)

정의

  • Redis는 Remote(원격)에 위치하고 프로세스로 존재하는 In-Memory 기반의 Dictionary(key-value) 구조 데이터 관리 Server 시스템이다. 

  • 여기서 key-value 구조 데이터란, mysql 같은 관계형 데이터가 아닌 비 관계형 구조로서 데이터를 그저 키-값 형태로 단순하게 저장하는 구조를 말한다.

  • 그래서 관계형 데이터베이스와 같이 쿼리 연산을 지원하지 않지만, 대신 데이터의 고속 읽기와 쓰기에 최적화 되어 있다.
    그래서 Redis는 일종의 NoSQL 로 분류되기도 한다.

  • 아키텍처상의 물리적 배치

    • 캐시는 CPU 내부의 레지스터나 L1, L2 캐시와 같은 하드웨어 계층부터, 웹 서비스에서 널리 쓰이는 분산 메모리 캐시 시스템(Redis, Memcached)에 이르기까지 시스템 전반에 걸쳐 다양한 위치에 배치된다.
    • 아키텍처 설계 시에는 데이터 접근의 빈도와 비용을 고려하여 최적의 위치를 선정한다.
  • 상위 추상화 관점의 역할

    • 논리적으로 캐시는 원천 데이터 저장소의 부분 집합(Subset)을 유지하는 역할을 수행한다.
    • 모든 데이터를 다루는 것이 아니라, 가장 가치 있는 일부 데이터를 선별하여 빠른 접근을 보장함으로써 전체 시스템의 효율성을 높인다.

Reomote의 의미

  • 레디스의 데이터는 로컬 저장소가 아닌 프로세스 형태로 관리되는 원격 저장소이다.

  • In-Memory vs Remote

    • 레디스는 데이터베이스를 보조기억장치가 아닌, 주기억장치에 위치하여 관리된다고 했는데, 데이터를 로컬이 아닌 원격 저장소로 관리된다는 말에 조금 햇갈려서 구분하였다.

    • In-Memory (무엇에?)

      • 저장 매체가 디스크가 아닌 RAM이라는 것을 의미.
    • Remote (어디에?)

      • 그 RAM이 내앱 내부가 아니라 네트워크 너머의 별도 서버에 있음을 의미.
    • 왜 내 메모리가 아닌 원격을 쓰는가?

      • 공유를 위해, 격리를 위해, 확장을 위해.

      • 데이터 공유

        • 여러 대의 서버가 하나의 레디스를 바라보며 동일한 데이터를 공유할 수 있다.
      • 독립적 생존

        • 애플리케이션 서버를 업데이트하거나 재시작해도, 별도의 프로세스로 떠 있는 Redis의 데이터는 안전하게 유지.
      • 유연한 확장

        • 데이터 양이 늘어나면 앱 서버는 그대로 두고 Redis 서버의 사양만 따로 높이거나 대수를 늘려 대응할 수 있다.

Redis Sentinel

  • 장애 감시 및 모니터링

    • 센티넬은 마스터 노드와 복제본 노드들의 동작 상태를 실시간으로 확인한다. 특정 노드가 응답하지 않거나 비정상적인 상태로 판단될 경우 이를 즉각적으로 감지하여 시스템의 가용성을 유지하기 위한 기초 데이터를 수집한다.
  • 자동 장애 조치

    • 마스터 노드에 장애가 발생하여 정상적인 서비스가 불가능하다고 판단되면 센티넬은 가용 가능한 복제본 노드 중 하나를 선택하여 새로운 마스터로 승격시킨다. 이 과정은 운영자의 개입 없이 자동으로 진행되어 서비스의 공백 시간을 최소화한다.
  • 상태 알림 및 통보

    • 모니터링 중인 인스턴스에 문제가 발생하거나 장애 조치가 수행되었을 때 관리자나 연결된 애플리케이션에 해당 이벤트를 알린다. 이를 통해 시스템 운영자는 현재 인프라의 상태 변화를 실시간으로 파악하고 필요한 후속 조치를 취할 수 있다.
  • 구성 정보 제공

    • 클라이언트 애플리케이션이 현재 마스터 노드의 주소를 직접 관리할 필요 없이 센티넬에게 문의하여 현재 유효한 마스터의 위치를 확인하게 한다. 장애 조치로 인해 마스터가 변경되더라도 클라이언트는 센티넬을 통해 항상 올바른 서버에 연결할 수 있다.
  • 과반수 합의 기반의 의사결정

    • 단일 센티넬의 오판으로 인한 잘못된 장애 조치를 방지하기 위해 여러 대의 센티넬이 투표를 통해 의사를 결정한다. 설정된 쿼럼 이상의 센티넬이 장애 상태에 동의해야만 실제 승격 절차가 시작되므로 시스템의 판정 신뢰성을 확보할 수 있다.

Redis Cluster

  • 데이터 샤딩을 통한 수평적 확장

    • 클러스터는 여러 대의 레디스 서버에 데이터를 분산하여 저장함으로써 단일 장비의 메모리 용량 한계를 극복하게 해준다. 이를 통해 서비스 규모 확장에 따라 서버를 추가로 투입하여 전체 시스템의 저장 용량과 처리 성능을 선형적으로 늘릴 수 있는 비즈니스 유연성을 제공한다.
  • 해시 슬롯 기반의 데이터 배치

    • 모든 데이터를 16384개의 가상 해시 슬롯으로 나누어 관리하며 각 노드에 이 슬롯들을 적절히 할당하는 방식을 취한다. 특정 키 값이 들어오면 해싱 알고리즘을 통해 어떤 슬롯에 속하는지 계산하고 해당 슬롯을 담당하는 노드에 데이터를 저장함으로써 데이터의 위치를 체계적으로 관리한다.
  • 고가용성을 위한 마스터와 복제 구조

    • 클러스터 내의 각 마스터 노드는 하나 이상의 복제본 노드를 가질 수 있어 데이터 유실 위험을 방지한다. 마스터 노드에 장애가 발생하 더라도 해당 슬롯을 공유하는 복제본 노드가 존재하기 때문에 시스템 전체의 중단 없이 서비스를 지속할 수 있는 안정성을 확보한다.
  • 자동 장애 조치 및 자가 치유 능력

    • 별도의 외부 감시 도구 없이 클러스터 내부의 노드들이 서로 통신하며 상태를 확인한다. 특정 마스터 노드의 장애가 과반수 노드에 의해 확인되면 가용 가능한 복제본을 새로운 마스터로 자동 승격시키는 과정을 통해 시스템의 자가 치유를 실현한다.
  • 무중단 노드 추가 및 삭제

    • 운영 중인 서비스의 가동을 멈추지 않고도 클러스터에 새로운 노드를 추가하거나 기존 노드를 제거할 수 있다. 슬롯 재배치 기능을 통해 데이터를 다른 노드로 이동시키면서 인프라 구조를 변경할 수 있어 급격한 트래픽 변화에 기민하게 대응할 수 있는 기술적 토대를 제공한다.

Redis Cache

Redis 캐시 체계

  • MySQL같은 관계형 데이터베이스(RDB)는 기본적으로 디스크(HDD/SDD)에 데이터를 저장한다. 반면, Redis는 모든 데이터를 메모리(RAM)에 저장한다.

    • 속도의 차이
      • 디스크에서 데이터를 찾는 것과 메모리에서 데이터를 찾는 것은 압도적인 속도 차이가 존재한다.
    • 데이터 구조
      • Redis는 Key-Value 형태의 단순한 구조를 가진다.
      • 복잡하게 얽힌 표(Table)를 뒤지는게 아니라, 이 이름(Key)에 해당하는 값(Value)을 달라고 요청하면 즉시 결과가 나온다.

데이터 캐싱 전략 (Caching Strategy)

읽기 중심 전략: Look Aside (Cache Aside)

  • 정의 및 동작 원리
    • 앱이 먼저 Redis를 확인한다. 데이터가 없으면(Cache Miss) 앱이 직접 DB에서 데이터를 읽어온 뒤, 이를 Redis에 저장한다.
  • 장점
    • 안정성
      • Redis가 갑자기 죽어도 DB가 살아있으므로 서비스가 완전히 멈추지 않는다.
    • 유연성
      • 원하는 데이터만 골라서 캐싱할 수 있다.
  • 단점
    • 첫 호출 지연
      • 초기 호출 시 무조건 DB를 거쳐야 하므로 느리다.
    • 데이터 불일치
      • DB 데이터는 바뀌었는데 Redis 데이터는 옛날 것일 수 있기 때문에 TTL 설정은 필수이다.
  • 적합한 상황
    • 대부분의 일반적인 웹 서비스.
    • 사용자 프로필, 게시판 목록 등.

읽기 중심 전략: Read Through

  • 정의 및 동작 원리
    • 앱은 Redis만 바라본다. 데이터가 없으면 Redis가 라이브러리나 자체 로직을 통해 DB에서 데이터를 가져와 업데이트한 뒤 앱에 전달한다.
  • 장점
    • 코드 단순화
      • 앱 로직에서 DB 호출 코드가 줄어들어 깔끔해진다.
    • 데이터 정합성
      • 캐시가 비어있을 확률이 줄어든다.
  • 단점
    • 강한 의존성
      • Redis가 죽으면 서비스 전체가 마비될 위험이 크다.
  • 적합한 상황
    • 읽기 부하가 매우 높은 서비스.
    • 데이터 일관성이 어느 정도 보장되어야 하는 특정 API 서버.

쓰기 중심 전략: Write Through

  • 정의 및 동작원리

    • 데이터를 저장할 때 Redis와 DB에 동시에 쓴다.
  • 장점

    • 최고의 신뢰성
      • 캐시와 DB가 항상 같은 데이터를 가진다.
      • 매우 높은 데이터 정합성을 가진다.
    • 재조회 속도
      • 방금 쓴 데이터를 바로 읽을 때 매우 빠르다.
  • 단점

    • 쓰기 지연
      • 매번 두 번씩 써야 하므로 저장 속도가 느려진다.
    • 리소스 낭비
      • 읽히지도 않을 데이터까지 모두 캐시에 저장되어 메모리가 부족해질 수 있다.
  • 적합한 상황

    • 금융권 결제 시스템
    • 개인 정보 수정
    • 데이터 하나하나가 정확해야 하는 곳.

쓰기 중심 전략: Write Back (Write Behind)

  • 정의 및 동작 원리

    • 모든 쓰기 요청을 일단 Redis에만 저장한다.
    • 이후 특정 시간마다 혹은 특정 양이 쌓이면 DB에 한꺼번에(Batch) 밀어 넣는다.
  • 장점

    • **압도적 성능
      • DB 쓰기 부하를 획기적으로 줄여준다.
      • 쓰기 작업이 많은 서비스에 최적이다.
  • 단점

    • 유실 위험
      • DB에 옮기기 전에 Redis 서버가 고장 나면 데이터가 사라진다.
  • 적합한 상황

    • 게임 실시간 랭킹, 좋아요 수 카운트, 로그 수집.
    • 짧은 시간 내에 폭발적인 데이터 수정을 견뎌야 하는 기능.
참고
  • 실무에서는 "Look Aside(읽기) + Write Around(쓰기)" 조합을 80% 이상 사용한다고 한다. 이유는 당연하게도 가장 안전하기 때문이다. (Write Around는 DB에만 먼저 쓰는 방식이다.)
  • 하지만 서비스가 "실시간으로 수만 명의 유저가 동시에 '좋아요'를 누르는 SNS"라면 저는 주저 없이 Write Back을 설계에 넣는 것이 좋을 것이다.
  • DB가 그 부하를 견디지 못하고 뻗어버리는 것보다, 약간의 데이터 유실 가능성을 감수하더라도 서비스가 돌아가게 만드는 것이 비즈니스적으로 이득일 때가 많기 때문이다.
  • 서비스의 안정성이 중요한가, 속도가 중요한가 등의 성격에 따라 가장 적합한 전략을 선택해야 한다.

Redis 캐시 처리 프로세스

Look-aside / Cache Aside

  • 애플리케이션이 캐시를 먼저 확인하고, 데이터가 없을 때만 DB에 접근하는 가장 일반적인 방식이다.

  • 프로세스

    • 애플리케이션 -> 캐시 확인 -> (Miss 발생) -> DB 데이터 조회 -> 캐시 데이터 저장 -> 결과 반환

Read-through

  • 캐시가 DB와의 동기화를 직접 담당하여 애플리케이션은 오직 캐시만을 통해 데이터를 읽는 방식이다.

  • 프로세스

    • 애플리케이션 -> 캐시 조회 -> (Miss 발생 시 캐시가 직접) DB 데이터 조회 -> 캐시 자체 업데이트 -> 결과 반환

Write-through

  • 데이터를 저장할 때 캐시와 DB에 동시에 업데이트하여 데이터 일관성을 보장하는 방식이다.

  • 프로세스

    • 애플리케이션 -> 캐시 데이터 저장 -> (즉시) DB 데이터 저장 -> 저장 완료 응답

Write-back / Write-behind

  • 데이터를 캐시에 먼저 모아두었다가 일정 시간이나 조건에 따라 DB에 일괄 저장하는 방식이다.

  • 프로세스

    • 애플리케이션 -> 캐시 데이터 저장 -> (일정 기간/량 대기) -> DB에 배치(Batch) 저장 -> 저장 완료 응답

Redis의 싱글 스레드

  • Redis는 싱글 스레드이다.
    • Redis는 한 번에 하나의 명령어만 처리한다.
    • 정확히 말하면, Redis의 명령어를 실행하는 핵심 로직이 싱글 스레드이다.
    • Redis 4.0 이전에는 정말 거의 모든 작업이 싱글 스레드였지만, 4.0 이후에는 무거운 데이터를 지우는 작업(UNLINK) 등은 백그라운드 스레드에서 처리하기 시작했다.
    • Redis 6.0 이후에는 네트워크 패킷을 읽고 쓰는 I/O 부분에 멀티 스레드를 도입했다.
    • 하지만 여전히 명령어 실행은 싱글 스레드가 순차적으로 처리하는 이유는 무엇일까?
    • 가장 우선 당연하게도 멀티 스레드를 사용하지 않아도 충분히 압도적으로 빠른 속도를 보장하기 때문이다.
    • 멀티 스레드로 만들면 데이터를 수정할 때마다 Lock(잠금)을 걸어야 한다.
    • 이 잠금을 관리하는 비용(Context Switching, Lock Contention)이 싱글 스레드가 줄 세워서 처리하는 것보다 훨씬 비싸기 때문이다.
    • CPU가 아닌 메모리와 네트워크 대역폭이 실제 병목이라는 점을 간파한 설계이다.

I/O 멀티플렉싱 (Non-blocking I/O)

  • 스레드가 하나면 수 만명의 클라이언트가 동시에 요청할 때 앞사람 끝날때까지 기다려야하지 않을까?

  • 식당으로 비유해보자.

    • 일반적인 방식(Blocking I/O)
      • 점원 한 명이 손님 한 명의 주문이 끝날 때까지 옆에서 가만히 기다린다.
      • 손님이 메뉴를 고민하면 점원도 같이 멍하게 서 있는다.
    • I/O 멀티플렉싱 방식
      • 점원은 한 명(싱글 스레드)인데, 모든 테이블에 을 설치한다.
      • 점원은 카운터에 가만히 있다가, 벨이 울리는(이벤트 발생) 테이블로만 달려가서 주문을 처리한다.
  • 즉, 수 많은 클라이언트 연결(소켓)을 열어두고, 데이터가 실제로 도착한 연결만 골라내서 순차적으로 처리하는 방식이다.

  • 실제 기술적으로는 epoll이나 kqueue같은 시스템 함수를 사용해 지금 일할 준비가 된 소켓들만 알려줘라고 OS에 물어 보는 것이다.

이벤트 루프 (Event Loop)

  • I/O 멀티플렉싱에서 언급한 벨소리를 듣고 실제로 일을 처리하는 프로세스가 바로 이벤트 루프이다.
  • 이벤트 루프는 아주 단순한 루프(Loop)를 돈다.
  1. 지금 데이터가 들어온 연결이 있는지 I/O 멀티플렉서에게 물어본다.
  2. 연결이 있다면 그 요청들을 큐(Queue)에 순서대로 담는다.
  3. 담긴 요청을 하나씩 꺼내서 실행한다.
  4. 다 하면 다시 1번으로 돌아간다.
  • 이 과정은 순식간에 일어나기 떄문, 클라이언트 입장에서는 마치 Redis가 자신만을 위해 일하고 있는 것 처럼 느껴질 것이다.

epoll과 kqueue의 배경: select 방식의 문제

  • 상황

    • 서버에 1만 명의 클라이언트가 연결되어 있다.
  • select 방식

    • 점원이 1번 테이블부터 1만 번 테이블까지 일일이 돌아다니며 묻는다.
    • 주문하실 분?, 없나요?, 주문하실 분?
  • 문제점

      1. 정작 주문하는 사람은 1~2명뿐인데, 1만 번을 다 돌아야 한다.
      1. 한 번 돌고 나면 점원이 힘이 빠져서(자원 소모), 다음번에 또 1만 번을 돌아야 한다.
    • O(N)의 시간 복잡도

epoll

  • 리눅스 개발자들이 이건 너무 비효율적이라고 판단하며 만든 것이 바로 epoll이다.

  • 핵심 원리

    • 점원이 돌아다니지 않는다.
    • 대신 주문 대기 명단(Ready List)를 카운터에 둔다.
  • 동작 원라

    • 손님이 벨을 누르면, 운영체제가 그 손님의 번호를 '주문 대기 명단'에 딱 적어준다.
    • 점원(서버 프로세스)은 카운터에 가서 지금 명단에 적힌 사람 누구인가 딱 한 번 물어본다.
    • 명단에 적힌 1~2명만 즉시 처리한다.
  • 장점

    • 연결된 클라이언트가 1만 명이든 100만 명이든, 실제로 움직이는 사람 수만큼만 일하면 된다.
    • O(1)에 가까운 효율성

kqueue

  • 특징

    • epoll과 거의 유사한 이벤트 기반 방식이다.
  • 차이점

    • epoll보다 조금 더 유연하고 광범위하다.
    • epoll은 주로 네트워크 소켓의 변화를 감시하는 데 특화되어 있다면, kqueue는 파일 수정, 신호(Signal) 발생 등 운영체제 내부의 온갖 사건들을 다 감시할 수 있다.
참고
  • select가 순차접근이라면 epoll과 kqueue 방식은 메모리의 임의접근이라고 이해하면 좋다.

  • epoll과 kqueue를 비교하여 좀 더 다양한 감지가 가능한 kqueue가 더 좋은 것이 아닌가 할 수 있지만, 여기서 집중과 선택을 간과해서는 안된다. 기업용 서버(웹 서버, DB서버 등)에서 가장 중요한 건 결국 수만 명의 동시 접속자를 처리하는 능력이다. epoll은 이 하나에만 집중해서 매우 뛰어난 성능을 보여준다.

  • 또한 전 세계 서버 OS의 90% 이상이 리눅스이다. 자연스럽게 epoll에 최적화된 고성능 라이브러리(Ningx, Redis 등)들이 많이 나왔고, 이것이 기술 생태계를 장악하게 된 것이다.

다양한 데이터 타입 (Data Structures)

  • String

    • 가장 단순한 타입이다.
    • 텍스트, 숫자, 심지어 이미지 같은 이진 데이터(Binary)도 넣을 수 있다.
    • 주로 HTML 페이지 통째로 캐싱하거나, 세션 정보를 저장할 때 사용한다.
    • 성능 영향: O(1)이다. 즉, 데이터가 얼마나 많든 찾는 속도가 일정하다.
  • Hash

    • 하나의 키 안에 여러 개의 필드-값 쌍을 가진다.
    • 유저 프로필, 상품 정보처럼 속성이 여러 개인 데이터를 저장할 때 최적이다.
    • 전체를 가져오지 않고 특정 필드만 수정/조회(O(1))할 수 있어 효율적이다.
    • 내부적으로 ZipList라는 구조를 사용하여, 데이터 양이 적을 때는 메모리를 극단적으로 아껴준다.
  • Sorted Set

    • 값(Member)과 점수(Score)를 함께 저장하며, 점수순으로 자동 정렬된다.
    • 실시간 인기 검색어, 게임 랭킹, 최근 본 상품 목록 등에 사용된다.
    • 정렬을 유지해야 하므로 추가/삭제 시 O(log N)의 시간이 걸린다.
    • 데이터가 수백만 건일 때 전체 범위를 조회하면 싱글 스레드인 Redis가 한동안 멈출 수 있다.
    • 그래서 항상 ZRANGE 같은 명령어로 페이징 처리를 하는 설계가 필수이다.
  • Set & List

    • Set

      • 중복을 허용하지 않는다.
      • 오늘 방문한 고유 사용자 수(UV)를 체크하거나 공통 친구 추천같은 집합 연산에 쓰인다.
    • List

      • 데이터가 들어온 순서를 보장합니다.
      • 캐시보다는 주로 메시지 큐(Job Queue)나 최근 공지사항 등의 타임라인을 캐싱할 때 사용합니다.

데이터 타입이 캐시 전략에 주는 3가지 영향

  • 시간 복잡도의 O(N)은 피해야한다.
    • Redis는 싱글 스레드라고 이야기했듯이 특정 데이터 타입의 명령어가 O(N)(데이터 개수만큼 시간이 걸리는 작업)이라면, 그동안 다른 클라이언트들은 모두 대기해야 한다.
    • 예: List에서 특정 값을 찾으려고 전체를 뒤지는 작업은 캐시 서버 전체를 마비시킬 수 있다.
  • 메모리 단편화와 효율성
    • 각 데이터 타입은 내부적으로 데이터를 저장하는 방식(Encoding)을 최적화한다.
    • 예를 들어 Hash는 데이터가 적으면 압축해서 저장하다가, 많아지면 검색이 빠른 해시 테이블로 변신한다.
    • 이 메커니즘을 이해하면 비용 절감 계획을 세울 수 있다.
  • 원자적 연산 (Atomic Operation)
    • 각 데이터 타입별로 제공되는 명령어(예: Sorted Set의 점수 올리기 ZINCRBY)는 그 자체로 원자성을 가진다.
    • 조회하고, 계산하고, 다시 저장하는 3단계를 거칠 필요 없이, 한 번의 명령으로 끝난다.
    • 이는 동시성 이슈를 원천 차단하는 캐시 설계의 핵심이다.

영속성 (Persistence): RDB vs AOF

  • 기본적으로 Redis는 메모리 기반이라 전원이 나가면 데이터가 날아간다.
  • 그래서 이를 방지하기 위한 보험이 필요한데, 그것이 바로 영속성(Persistence) 관리 전략RDBAOF이다.

RDB (Redis DataBase)

  • RDB는 특정 시간마다 메모리에 있는 데이터 전체를 사진 찍듯이 그대로 복사해서 디스크에 저장하는 방식이다.
  • 특징
    • 데이터 그 자체를 압축해서 저장하므로 파일 크기가 작다.
    • 복구가 매우 빠르다.
  • 단점
    • 데이터를 1시간 간격으로 복사 하도록 설정하면, 10시59분에 서버가 터졌다면, 10시부터 10시 59분까지의 데이터는 영구적으로 사라진다.

AOF (Append Only File)

  • AOF는 Redis에서 일어나는 모든 쓰기 작업(INSERT, UPDATE 등)을 파일 끝에 계속 추가하는 방식이다.

  • 특징

    • 안전성
      • 거의 실시간으로 기록하기 때문에 서버가 꺼져도 마지막 순간까지의 기록이 남아있다.
    • 파일이 무거움
      • 1,000번 수정한 데이터라면 1,000번의 기록이 다 남기 때문에 파일이 무겁다.
    • 느린 복구 속도
      • 수첩에 적힌 1,000번의 기록을 처음부터 끝까지 다시 실행(Replay)해서 칠판을 채워야 하니까요.

실무에서는 어떻게 쓸까?

  • 실무에서는 둘다 사용하는 Redis(4.0 버전 이후)의 RDB + AOF 혼합 방식을 사용한다.
  1. 평소에는 AOF로 데이터를 촘촘하게 기록해서 안전성을 챙긴다.

  2. 중간중간 AOF rewrite라는 과정을 통해 로그를 압축하고, 전체적인 상태는 RDB처럼 스냅샷을 찍어둔다.

  3. 서버 복구 시에는 RDB 스냅샷으로 뼈대를 빠르게 세우고, 마지막 스냅샷 이후의 기록만 AOF 로그로 채운다.

TTL (Time To Live)

  • 정의

    • TTL은 특정 데이터가 Redis 메모리에 머물 수 있는 만료 시간을 의미한다.
    • 이 시간이 지나면 Redis는 해당 데이터를 자동으로 삭제한다.
  • 단위

    • 보통 초 또는 밀리초 단위로 설정한다.
  • 설정 방식

    • EXPIRE key 3600 (이 키를 1시간 뒤에 삭제)
  • Redis는 만료된 데이터를 어떻게 치울까?

    • Redis는 싱글 스레드인데 1000만개의 데이터 유통기한을 매초 일일히 체크하면 Redis는 다른 일을 못 하고 멈춰버릴 것이다. 그래서 Redis는 두 가지 방식을 섞어 쓴다.
  • Passive Deletion (수동적 삭제)

    • 사용자가 데이터를 조회(Get)할 때 확인하는 방식이다.
    • 점원이 삼각김밥을 일일이 검사하지 않다가, 손님이 집어 오면 그때 유통기한을 보고 아, 이건 유통기한이 지났네요. 이건 버리겠습니다처럼 삭제하는 방식이다.
    • 장점
      • CPU 사용량을 최소화한다.
  • Active Deletion (능동적 삭제)

    • 하지만 아무도 조회하지 않는 데이터는 영원히 메모리를 차지하는 문제가 있을 것이다. 그래서 Redis는 주기적으로 샘플링을 한다.
    • 1초에 10번 정도, 만료된 키들을 무작위로 뽑아서 검사하고 지운다.
    • 전체 데이터의 약 25% 이상이 만료된 상태라면, 메모리를 확보하기 위해 이 작업을 더 열심히 수행한다.

TTL의 비즈니스에서의 중요성

  • 단순히 메모리 절약뿐만 아니라, 실제 서비스 설계에서 TTL은 아래와 같은 핵심 역할을 한다.

  • 세션 관리

    • 유저가 로그인을 하면 30분 동안만 세션을 유지하고, 활동이 없으면 자동으로 로그아웃 처리할 때 필수이다.
  • 일회성 인증 번호

    • SMS 인증 번호를 발송하고 3분 이내에 입력하세요라고 할 때, 3분이 지나면 자동으로 사라지게 만든다.
  • 캐시 신선도 유지

    • 실시간 뉴스나 환율 데이터를 1분마다 갱신해야 한다면, TTL을 60초로 설정해 두어 항상 최신 데이터를 유지하게 한다.

Cache Stampede

  • 만약 유명 연예인의 프로필 정보 캐시의 TTL이 1시간인데, 이 1시간이 끝나는 순간 수만 명의 팬이 동시에 접속하면 어떻게 될까?
  1. 캐시는 만료되어 사라짐.
  2. 수만 개의 요청이 한꺼번에 원본 DB(MySQL 등)로 몰려감.
  3. DB가 과부하로 병목 발생.
  • 이를 캐시 스탬피드(Cache Stampede) 현상이라고 한다.
  • 중요한 데이터의 TTL은 정확히 1시간(3600초)으로 설정하기보다, 3600초에 약간의 난수(Jitter)를 더해 3550~3650초 사이에서 랜덤하게 만료되도록 설계해야 한다. 그래야 요청이 분산되어 시스템이 안전하다.

메모리 관리 정책 (Eviction Policy)

  • Redis는 메모리가 설정된 최대치(maxmemory)에 도달하면, 새로운 데이터를 넣기 위해 기존 데이터를 강제로 지워야 한다. 이때 어떤 기준으로 지울지가 바로 정책의 핵심이다.

  • LRU (Least Recently Used)

    • 가장 오랫동안 사용되지 않은 것"을 버린다. (최근성 중심)
  • LFU (Least Frequently Used)

    • 가장 사용 횟수가 적은 것"을 버립니다. (빈도 중심)

주요 메모리 관리 정책

  • noeviction (안전 제일형)

    • 전략: 아무것도 지우지 않는다.
    • 결과: 메모리가 꽉 차면 새로운 입력을 거부하고 에러를 낸다.
    • 데이터들이 전부 중요하여 절대 삭제되면 안 되는 특수한 경우에 사용한다.(사실 캐시 용도로는 거의 쓰지 않는다.)
  • allkeys-lru (대중적인 캐시형)

    • 전략: 모든 데이터 중 가장 오랫동안 안 쓴 데이터부터 버린다.
    • 가장 무난하고 성능이 좋아 대부분의 서비스에서 기본으로 사용한다.
  • volatile-lru (보험 가입형)

    • 전략: TTL(만료 시간)이 설정된 데이터들 중에서만 안 쓴 데이터를 골라 버린다.
    • "만료 시간이 초과한 것은 버려도 되지만, 기한이 없는 중요한 설정 데이터는 절대 건드리지 않도록 설정 할 때 사용한다.
  • allkeys-lfu (데이터 효율형)

    • 전략: 전체 데이터 중 가장 적게 쓰인 데이터를 버린다.
    • 어쩌다 한 번 최근에 읽힌 것보다, 꾸준히 많이 읽히는 데이터를 살리는 게 이득이다라고 판단될 때 유리하다.
참고
  • 이 정책을 잘못 고르면 서비스에 어떤 장애가 생기는지 아는 것으로 역량을 판가름할 수 있다.

  • OOM(Out Of Memory)

    • 만약 noeviction 상태인데 서비스에 사용자가 몰리면, DB에 데이터를 넣지 못해 결제나 로그인이 통째로 실패할 수 있다.
  • 캐시 히트율(Cache Hit Rate) 하락

    • 우리 서비스 성격에 맞지 않는 정책을 쓰면, 정작 필요한 데이터가 자꾸 삭제되어 서버가 느려진다.(예: 자주 찾는 공지사항이 최근에 안 읽혔다고 삭제되는 경우)

고가용성 (High Availability): Sentinel

  • 정의 및 존재 이유

    • 고가용성 (High Availability)
      • 서비스가 중단되지 않고 지속적으로 운영될 수 있는 능력을 의미한다. 레디스는 메모리 기반 저장소이므로 서버 다운 시 데이터 접근이 불가능해지는데, 이를 방지하기 위해 센티넬이 투입된다.
    • 자동 장애 조치 (Automatic Failover)
      • 마스터 서버에 장애가 발생했을 때 사람이 수동으로 명령어를 입력하지 않아도, 시스템이 스스로 판단하여 복제 서버(Replica)를 마스터로 승격시키는 메커니즘이다.
  • 핵심 감시 메커니즘

    • 모니터링 (Monitoring)
      • 센티넬 노드들은 주기적으로 마스터와 슬레이브 노드에 PING 메시지를 보내 생존 여부를 확인한다.
    • 쿼럼 (Quorum)과 다수결
      • 특정 노드의 장애 여부를 판단하기 위한 최소 투표수이다. 네트워크 일시 오류로 인한 오판을 막기 위해 반드시 여러 대의 센티넬이 동의해야 장애로 인정된다.
    • 홀수 구성 (Odd Number of Nodes)
      • 투표 시 과반수 결론을 내기 위해 보통 3대, 5대 등 홀수로 구성한다. 이는 '분할 뇌(Split Brain)' 현상을 방지하고 의사결정의 무결성을 보장하기 위함이다.
  • 장애 판정의 단계

    • SDOWN (Subjective Down, 주관적 다운)
      • 특정 센티넬 한 대가 마스터 서버로부터 응답을 받지 못했을 때 내리는 개별적인 판단이다. 아직 공식적인 장애는 아니다.
    • ODOWN (Objective Down, 객관적 다운)
      • 설정된 쿼럼 이상의 센티넬들이 동시에 해당 마스터의 장애(SDOWN)를 보고했을 때 성립되는 최종 상태다. 이 단계가 되어야 실제 Failover 프로세스가 시작된다.
  • 장애 조치 이후의 역할

    • 구성 정보 제공자 (Configuration Provider)
      • 서비스 어플리케이션(Client)이 서버 주소를 직접 들고 있는 것이 아니라 센티넬에게 "지금 마스터가 누구냐"고 물어보게 함으로써, 서버가 교체되어도 중단 없이 올바른 주소로 연결되도록 돕는다.

고가용성 (High Availability): Cluster

  • 정의 및 핵심 목적

    • 분산 저장소 (Distributed Store)
      • 데이터를 여러 대의 서버(Node)에 쪼개어 저장하는 방식이다. 단일 서버의 메모리 용량 한계를 극복하기 위해 사용된다.
    • 수평적 확장 (Scale-out)
      • 서버를 옆으로 계속 이어 붙여서 전체 시스템의 처리량(Throughput)을 늘리는 것을 목적으로 한다. 센티넬이 가용성에 치중했다면, 클러스터는 가용성과 성능 확장을 동시에 추구한다.
  • 데이터 분산 방식

    • 샤딩 (Sharding)
      • 거대한 데이터를 특정 기준에 따라 조각내어 나누는 기술이다. 레디스 클러스터는 모든 키를 16,384개의 조각으로 나눈다.
    • 해시 슬롯 (Hash Slots)
      • 데이터를 나누는 단위이다. CRC16(key) % 16384라는 공식을 통해 모든 데이터는 반드시 특정 슬롯에 할당되며, 각 마스터 노드는 이 슬롯들을 나누어 가진다.
  • 고가용성 및 복제 구조

    • 마스터-슬레이브 통합 (Built-in Replication)
      • 클러스터 내의 각 마스터 노드는 자신만의 복제본(Replica)을 가진다. 센티넬 없이도 클러스터 내부의 노드들끼리 서로 상태를 감시하며, 특정 마스터가 죽으면 그 마스터의 슬레이브가 자동으로 승격된다.
    • 풀 메시 구조 (Full Mesh Topology)
      • 모든 노드가 서로 연결되어 상태를 공유(Gossip Protocol)하므로, 별도의 감시 서버(Sentinel)가 필요하지 않다.
  • 클라이언트와 통신 방식

    • 스마트 클라이언트 (Smart Client)
      • 클라이언트는 클러스터의 슬롯 정보를 미리 알고 있어야 한다. 만약 잘못된 노드에 데이터를 요청하면, 서버는 "이 데이터는 저쪽 노드에 있어"라는 MOVED 에러와 함께 올바른 주소를 알려준다(Redirection).
    • 부분 장애 격리
      • 특정 마스터와 그 슬레이브가 모두 죽더라도, 나머지 마스터 노드들이 담당하는 슬롯의 데이터는 여전히 서비스가 가능하다. 즉, 장애가 시스템 전체로 퍼지는 것을 방지한다.

  • 실무 운영 시 고려해야 할 핵심 요소
    • Cache Aside의 Cold Start
      • 서비스 배포 직후나 캐시 재시작 시점에 캐시가 비어 있어 모든 요청이 DB로 쏠리는 현상이 발생할 수 있으므로, 초기 데이터를 미리 채워두는 Warm-up 작업이 필요하다.
    • 데이터 휘발성 대비
      • 메모리 특성상 전원 차단 시 데이터가 손실될 수 있으므로, 중요한 데이터는 RDB나 AOF 설정을 통해 디스크에 주기적으로 백업하여 복구 능력을 확보해야 한다.
    • 캐시 스탬피드 (Cache Stampede)
      • 대규모 트래픽 환경에서 특정 키가 만료되는 순간 수많은 요청이 동시에 DB로 전달되는 현상을 방지하기 위해 TTL 설정 시 임의의 변동 값을 더하는 전략이 유효하다.

Redis Sentinel vs Redis Cluster: 아키텍처 선택 가이드

  • 핵심 메커니즘 비교
    • 데이터 관리 방식
      • Sentinel: 모든 데이터를 단일 마스터에 저장하며, 복제본은 동일한 데이터를 복사하여 보유함. 용량 확장에 한계가 존재함.
      • Cluster: 데이터를 샤드(Shard) 단위로 쪼개어 여러 마스터에 분산 저장함. 서버 대수에 비례하여 용량 확장이 가능함.
    • 장애 감지 및 조치
      • Sentinel: 외부의 센티넬 노드들이 마스터를 감시하고 투표를 통해 장애 조치를 수행함.
      • Cluster: 외부 감시자 없이 노드들끼리 서로 상태를 주고받으며 스스로 장애를 판단하고 조치함.
  • 상황별 적합성 평가

    • Sentinel 사용이 유리한 경우
      • 데이터 규모: 단일 서버 메모리(예: 64GB 미만) 내에서 충분히 처리가 가능한 경우.
      • 운영 복잡도: 소규모 팀에서 복잡한 설정 없이 안정적인 고가용성(HA)만 확보하고 싶을 때 적합함.
      • 일관성 중시: 멀티 키 연산(MGET, MSET 등)을 제약 없이 사용해야 하는 비즈니스 로직에 유리함.
    • Cluster 사용이 유리한 경우
      • 대규모 트래픽: 초당 수십만 건 이상의 요청이 발생하여 단일 CPU의 성능을 초과할 때 필수적임.
      • 데이터 가용량: 수백 GB 이상의 대용량 캐시 데이터가 필요하여 수평적 확장이 요구될 때 사용함.
      • 무중단 확장: 서비스 중단 없이 실시간으로 노드를 추가하여 용량을 늘려야 하는 성장기 서비스에 적합함.
  • 실무 관점의 요점 정리

    • 운영 리소스의 차이
      • Sentinel은 구조가 단순하여 트러블슈팅이 용이하나, Cluster는 데이터가 흩어져 있어 장애 발생 시 원인 파악 및 슬롯 재할당(Resharding) 등의 관리가 까다로움.
    • 클라이언트 라이브러리 의존성
      • Cluster는 '스마트 클라이언트'를 지원하는 라이브러리 사용이 강제되며, 데이터가 여러 노드에 걸쳐 있을 시 '멀티 키 명령'이 실패할 수 있다는 설계상 제약이 존재함.
    • 최종 선택의 기준
      • 초기 단계나 일반적인 웹 서비스는 Sentinel로 충분하며, 글로벌 스케일 혹은 빅데이터 기반의 서비스라면 처음부터 Cluster로 설계하는 것이 실무적인 정석임.

Redis Cache를 사용한다는 것은 어떤 의미일까?

  • 메모리 중심의 고속 데이터 처리

    • In-Memory 아키텍처
      • 데이터를 영구 저장 장치인 디스크가 아닌 휘발성 메모리(RAM)에 저장하는 방식이다. 물리적인 디스크 I/O 과정이 생략되므로 밀리세컨드 단위의 압도적인 응답 속도를 보장하며 사용자 경험을 극대화한다.
  • 백엔드 인프라의 안정성 확보

    • 데이터베이스 부하 분산 (Offloading)
      • 반복적으로 발생하는 동일한 쿼리 요청을 메인 DB까지 보내지 않고 캐시 계층에서 즉시 반환하는 전략이다. 이를 통해 메인 데이터베이스의 CPU와 메모리 리소스를 보존하고 시스템 전반의 병목 현상을 방지한다.
  • 휘발성 데이터의 전략적 운용

    • TTL (Time To Live)
      • 데이터에 유효 기간을 설정하여 시간이 지나면 자동으로 삭제되도록 관리하는 기능이다. 세션 정보, 인증 토큰, 일시적인 검색 결과 등 생명 주기가 짧은 데이터를 효율적으로 관리하고 메모리 고갈을 예방한다.
  • 데이터 무결성과 동시성 제어

    • 싱글 스레드 기반 원자성 보장
      • 모든 명령을 순차적으로 처리하여 여러 요청이 동시에 들어와도 데이터가 꼬이는 경합 현상을 방지한다. 선착순 이벤트나 실시간 조회수 집계처럼 데이터의 정확도가 중요한 비즈니스 로직에서 신뢰성을 제공한다.

Application Redis를 Cache로 사용한다면 어떻게 사용해볼 수 있을까?

  • 캐시 읽기 전략 (Read Strategies)

    • Look-aside (Cache Aside)
      • 애플리케이션이 데이터를 찾을 때 캐시를 먼저 확인하고, 데이터가 없으면 DB에서 조회한 뒤 이를 다시 캐시에 저장하는 방식임. 구조가 단순하여 실무에서 가장 많이 선호되며, 캐시에 장애가 발생해도 서비스가 중단되지 않고 DB를 통해 직접 데이터를 제공할 수 있는 안전성을 가짐.
    • Read-through
      • 애플리케이션이 캐시에게만 데이터를 요청하고, 캐시 제공자가 DB와의 데이터 동기화를 전담하는 방식임. 애플리케이션 코드가 간결해지지만 캐시에 대한 의존도가 높아지므로 캐시 가용성 확보가 필수적임.
  • 캐시 쓰기 전략 (Write Strategies)

    • Write-through
      • 데이터를 저장할 때 캐시와 DB에 동시에 업데이트하는 방식임. 데이터 일관성이 매우 높다는 장점이 있으나, 매번 두 곳에 모두 써야 하므로 쓰기 성능이 상대적으로 느려질 수 있음.
    • Write-back (Write-behind)
      • 데이터를 먼저 캐시에만 저장하고 일정 시간이나 일정량이 쌓이면 나중에 DB에 일괄 반영하는 방식임. 쓰기 작업이 빈번한 시스템에서 성능을 극대화할 수 있으나, DB에 반영되기 전 Redis 장애가 발생하면 데이터가 유실될 위험이 존재함.
    • Write-around
      • 모든 데이터는 DB에만 저장하고 캐시 미스가 발생할 때만 캐시를 업데이트하는 방식임. 쓰기 성능은 빠르지만, 데이터가 처음 조회될 때까지는 캐시 효과를 볼 수 없다는 특징이 있음.
  • 데이터 효율 관리 및 운영 핵심

    • TTL (Time To Live) 설정
      • 캐시 데이터의 유효 기간을 설정하여 메모리 낭비를 방지하고 데이터의 신선도를 유지함. 데이터의 특성에 따라 짧은 TTL(자주 변하는 정보)과 긴 TTL(정적인 정보)을 적절히 배분하는 설계가 필요함.
    • 메모리 제거 정책 (Eviction Policy)
      • 메모리가 가득 찼을 때 어떤 데이터를 우선적으로 지울지 결정하는 규칙임. 최근에 가장 적게 사용된 데이터를 지우는 LRU(Least Recently Used)나 사용 빈도가 낮은 데이터를 지우는 LFU(Least Frequently Used)가 주로 활용됨.
    • 캐시 스탬피드 (Cache Stampede) 방지
      • 대규모 트래픽 환경에서 특정 키가 만료되는 순간 수많은 요청이 한꺼번에 DB로 몰리는 현상을 방지해야 함. TTL에 무작위 값을 추가하거나(Jitter), 만료 전 미리 갱신하는 기법을 통해 시스템 안정성을 확보함.

Redis 메시징 시스템: Pub/Sub 및 Streams의 기술적 비교

  • 레디스는 보통 데이터를 빠르게 저장하는 캐시로만 생각하기 쉽지만 메시징 시스템으로서의 활용도도 매우 높다.
  • 현대적인 아키텍처, 특히 MSA 환경에서는 서비스 간의 대화가 필수적이다.
  • Redis는 이 대화를 아주 빠르고 가볍게 중재해주는 역할을 한다.
  • Redis의 메시징을 구현하느방식은 크게 두 가지 철학으로 나뉜다.
  • 지금 당장 듣고 있는 사람에게만 전달할 것인가?, 나중에라도 확인할 수 있게 기록을 남길 것인가의 차이이다.

HTTP/TCP VS 메시징 시스템

  • HTTP/TCP

    • 일반적인 HTTP/TCP 방식은 상태적 의존성을 갖는다.
    • 요청이 보낸 쪽(Client)은 응답이 올 때까지 연결을 유지하며 기다려야 한다.(Blocking)
    • 만약 메시지를 받는 쪽(Server)이 응답을 주기 전에 네트워크가 끊기거나 서버가 죽으면 요청을 보낸 쪽은 에러를 뱉고 작업을 실패 처리한다.
    • 둘 중 하나만 잘못되어도 전체 프로세스가 깨지는 문제가 있는 것이다.
  • Messaging System

    • 반면 Redis같은 메시징 시스템은 이 연결의 고리를 끊는다.
    • 이를 전문 용어로 관심사 분리 또는 Decoupling이라고 한다.
    • 연결의 분리
      1. 송신자(A)는 Redis와만 연결해서 메시지를 던진다.
      2. 수신자(B)는 나중에 Redis와 연결해서 메시지를 가져간다.
      • 즉, A와 B가 동시에 서로를 바라보며 연결되어 있을 필요가 전혀 없다.
    • 송신자는 레디스에 잘들어갔나 확인만하면 된다. 수신자는 지금 점검 중이든, 휴가 중이든 상관하지 않는다. 업무의 주도권이 송신자에게 있는 셈이다.
  • 그럼 무조건 메시징이 좋은건가?

    • 즉각적인 결과가 필요하면 = Synchronous
      • 예시로 사용자가 비밀번호를 변경했으면 변경되었습니다.라는 메시지를 바로 확인해야 한다. 이럴 땐 HTTP가 맞다.
    • 결과보다 수행 자체가 중요하면 = Asynchronous
      • 사용자가 회원가입을 했다. 가입 축하 이메일은 1초 뒤에 가든 10초 뒤에 가든 가입 프로세스 자체에는 지장이 없다. 이럴 땐 Redis 메시징을 쓴다.

Pub/Sub (발행/구독 모델)

  • Pub/Sub은 Publish(발행)와 Subscribe(구독)의 약자이다.

  • 이 방식의 가장 큰 특징은 송신자와 수신자가 서로 누군지 모른채, 오직 채널(Channel)이라는 주파수에만 집중한다는 것이다.

  • 동작 원리

    • Publisher
      • 특정 채널에 메시지를 던진다. 누가 듣고 있는지는 신경 쓰지 않는다.
    • Channel
      • 메시지를 전달되는 통로이다.
    • Subscriber
      • 특정 채널을 구독하고 있다가, 메시지가 오면 실시간으로 반응한다.
  • Pub/Sub의 핵심: Fire and Forget (던지고 잊어라)

    • Pub/Sub을 선택할 때 가장 중요하게 고려하는 키워드는 휘발성이다.
    • 라디오를 떠올려보자, 내가 지금 라디오를 켰는데, 10분 전에 나온 노래를 다시 들을 수 있는가? 당연히 불가능하다. Pub/Sub도 마찬가지이다.
    • 실시간성
      • 메시지가 발생하는 즉시 구독자들에게 쏜다. 속도가 매우 빠르다.
    • 비저장성
      • 메시지를 받은 사람이 아무도 없더라도, Redis는 그 메시지를 보관하지 않고 즉시 버린다.
    • 비연결성
      • 구독자가 잠시 네트워크가 끊겨서 메시지를 못 받았다면 그 메시지는 다시 받을 수 없다.
  • 현업

    • 데이터가 사라지는데 왜 쓰는가?라는 의문이 생길 수도 있지만, 매우 가벼운 특성으로 인해 아래와 같은 상황들에 매우 적합한 강점이 된다.

    • 실시간 채팅

      • 내가 단톡방에 메시지를 보냈을 때, 지금 접속 중인 사람들에게 즉시 전달되어야 할 때 가장 효율적이다.
    • 푸시 알림

      • 누가 내 게시물에 좋아요를 눌렀다.같은 알림은 굳이 DB에 무겁게 저장할 필요없이 실시간으로 전달하기 좋다.
    • 시스템 설정 변경 알림

      • 대기업 서버는 수천 대이다. 관리자가 설정 하나를 바꿨을 때, 수천 대의 서버가 설정값을 변경하라는 알림(Broadcasting)에 사용한다.

Streams & Lists (큐 모델)

  • Pub/Sub을 라디오 방송으로 비유했다면 Lists와 Streams는 기록이 남는 장부 또는 체계적인 우체국으로 비유할 수 있다.

  • Redis Lists

    • Redis Lists는 가장 고전적이고 직관적인 방식이다. FIFO 생각하면 된다.

    • 동작 원리

      • 데이터를 한쪽 끝에서 넣고(LPUSH), 다른 쪽 끝에서 뺴낸다.(RPOP)
    • 특징

      • 순서 보장 : 먼저 들어온 작업이 무조건 먼저 나간다
      • 단순성 : 구현이 매우 쉽다.
      • 한계 : 한 번 꺼내 간 데이터는 리스트에서 사라진다. 여러 명의 수신자가 같은 데이터를 동시에 처리하기엔 적합하지 않다.
    • 비즈니스 활용

      • 간단한 이메일 발송 대기열
      • 이벤트 당첨자 선착순 접수 리스트

Redis Streams

  • Lists가 그냥 이라면, Streams는 타임스탬프가 찍힌 영구 기록장이다. 아파치 카프카(Apache Kafka)라는 기술의 장점을 Redis 스타일로 가볍게 가져온 기술이다.

  • 동작 원리

    • 메시지가 들어올 때마다, 고유한 ID(시간 기반)가 부여되며, 기록이 계속 쌓인다.
  • 핵심 기능

    • **소비자그룹(Consumer Groups)

      • 여러 대의 서버가 하나의 스트림을 나눠서 읽을 수 있다.
      • 1번 서버는 A 작업을, 2번 서버는 B 작업을 해라는 분업이 완벽해진다.
    • 수신 확인(ACK)

      • 서버가 메시지를 가져갔는데, 처리 도중 에러가 나서 죽었다면?
      • Streams는 아직 처리 안됨으로 표시했다가 다른 서버가 다시 처리하게 해준다.
      • 데이터 유실 제로에 가까운 성능을 보여준다.
    • 과거 데이터 다시 읽기

      • 이미 읽은 데이터라도 특정 시점부터 다시 읽어올 수 있다.
참고
  • Lists는 빨리 처리하고 치울 일에 쓰고, Streams는 누가 무엇을 했는지 기록하며 체계적으로 나눠야 할 일에 쓰는 것이 적합하다.

  • 예시로, 서비스에서 화원가입 환영 문자 발송은 Lists로 충분하다.

  • 하지만, 실시간 로그 분석이나, 복잡한 주문 처리 프로세스라면 반드시 Streams를 고려해야 한다.

  • 현업에서는 Stream을 사용하는 덕분에 시스템 가용성을 대폭적으로 끌어올릴 수 있다.

Kafka나 RabbitMQ도 있는데 Redis를 사용하는 이유

  • 속도

    • 가장 큰 차이는 데이터가 어디에 저장되느냐이다.

    • Kafka/RabbitMQ

      • 기본적으로 메시지를 하드디스크에 기록하기 때문에, 안전하지만 물리적인 읽기/쓰기 시간이 걸린다.
    • Redis

      • 모든 것이 메모리(RAM)에 일어난다.
  • 운영의 간결함

    • Kafka
      • 엄청난 양의 처리량과 저장 용량을 처리할 수 있다.
      • 레디스랑 다르게 하드디스크에 데이터를 쌓기 때문에, 몇달치 또는 몇년치의 데이터를 저장하여 꺼내볼 수 있다.
      • 대기업에서 카프카 하나를 도입하려면 서버 비용, 개발자들이 카프사 설정법 또는 운영법을 새로 공부해야하는 학습 비용, 사내 보안팀에 가서 카프카용 포트를 열어달라는 요청을 하거나 방화벽 설정을 하 결제 받는 데만 일주일이 걸린다.
    • Redis
      • 이미 대부분의 서비스에서 캐시 용도로 레디스를 쓰고 있다.
      • 이미 있는 인프라를 그대로 활용하면 되기 때문에 추가 비용(인프라 비용, 인건비)이 거의 0에 수렴한다.
      • 만약 서비스에서 이미 레디스에서 로그인 유지하거나 데이터를 임시 저장하는 기술을 레디스로 사용하고 있다면 메시징 기능도 이미 사용중인 기능에 별다른 작업없이 추가 할수있다.
참고
  • 현업에서는 3가지 방식을 적절하게 섞어 쓴다.
    1. 사용자가 클릭했을 때 0.1초 안에 반응해야하는 실시간 알림은 Redis로 보낸다.
    2. 사용자의 결제 정보처럼 하나라도 누락되면 큰일 나는 데이터는 RabbitMQ로 안전하게 옮긴다.
    3. 하루에 수십억 건씩 쌓이는 사용자 행동 로그는 Kafka에 쌓아둔다.

루아 스크립팅 (Lua Scripting)

  • 동시성 제어: 원자성(Atomicity)

    • 레디스는 기본적으로 싱글 스레드로 동작한다.
    • 루아 스크립트가 실행되는 순간, 레디스는 이 스크립트가 끝날 때까지 다른 데이터는 못들오게 Lock을 건다.
    • 이를 원자성이라고 하며, 스크립트 내부의 여러 명령이 마치 단 하나의 명령처럼 묶여서 실행되기 때문에, 다른 서버가 중간에 데이터를 가로채거나 수정하는 동시성 사고가 물리적으로 불가능해진다.
  • 성능 최적화(RTT 감소, Round Trip Time)

    • 일반적인 방식은 서버와 레디스를 여러 번 왔다 갔다하며 데이터를 주고받지만, 루아 스크립트는 여러 명령을 하나의 묶음으로 포장해서 레디스에 한 번에 던진다.
    • 네트워크 왕복 시간을 획기적으로 줄인다.
    • 기존: (요청 -> 응답) × 5회 = 5단위 시간 소요
    • 루아: (스크립트 통째 전송 ->최종 결과 응답) × 1회 = 1단위 시간 소요
  • 복잡한 로직의 서버 사이드 처리

    • 데이터를 서버로 가져와 가공하고 다시 레디스에 저장하는 대신, 로직(Script)을 데이터가 있는 레디스 서버로 직접 보낸다.
    • Data를 Server로 보내며 처리하느냐, 아니면 Script를 들고 Redis에 가서 처리하느냐의 차이이다. 큰 데이터를 네트워크로 주고받을 필요가 없지니 시스템 전체가 가벼워지고 코드는 간결해진다.

확장 기능 (Redis Modules)

Bloom Filter

  • 블룸 필터는 특정 데이터가 여기에 있는지 없는지를 아주 빠르게 판단해 주는 확률적 자료구조이다.

  • 블룸 필터는 데이터를 직접 저장하지 않는다. 대신 아주 긴 0과 1로된 줄(비트 배열)을 사용한다.

  • 저장할 때

    • 데이터를 여러 개의 해시 함수에 통과시켜 나온 숫자 자리에 1을 표시한다.
  • 확인할 때

    • 찾으려는 데이터를 똑같은 해시 함수에 넣는다.
      • 그 자리들 중 하나라도 0이 있다면? -> 100% 없다고 판단.
      • 모든 자리가 1이라면? -> 있을 수도 있다. 가서 확인한다.
  • 캐시 관통(Cache Penetration) 방지

    • 악성 유저나 봇이 우리 DB에 없는 가짜 상품 ID를 수백만 번 요청한다고 가정하자.
    • 캐시에는 데이터가 없으니 매번 느린 DB까지 가서 확인해야 한다. 시스템은 커다란 부하를 받을 것이다.
    • 해결법
      • DB 앞에 블룸 필터를 둔다. 블룸 필터가 이 ID는 우리 DB에 확실히 없다.라고 커트해주면 DB까지 갈 필요가 없어진다.
  • 압도적인 메모리 절약

    • 수억 개의 데이터를 진짜로 다 저장하려면 수십 GB의 메모리가 필요하지만, 블룸 필터는 그 1%도 안되는 용량으로 수억 개의 데이터 존재 여부를 관리할 수 있다.
    • 인프라 비용 절감의 매우 큰 공신이다.
  • 거짓 양성 (False Positive)

    • 블룸 필터는 완벽하지 않다. 확률적 구조이기 때문에 없는데 있다고 실수하는 경우가 가끔있다.
    • 우연히 서로 다른 데이터들이 같은 비트 자리에 1을 표시하게 될 때 발생한다.
    • 없다고 한 것은 100% 정확하기 때문에 DB를 보호하는 데는 지장이 없다. 다만, 있다고 한 것만 DB에 가서 한 번 더 확인하면 된다.

HyperLogLog

HyperLogLog는 집합의 원소 개수(Cardinality, 카디널리티)를 추정하는 확률적 자료구조이다.

  • 압도적인 메모리 절약 (Efficiency)

    • 일반적인 방식(Set)
      • 사용자 ID(약 32바이트) 1억 개를 중복 제거하며 저장하려면 약 3GB 이상의 메모리가 필요하다.
    • HyperLogLog
      • 데이터가 100개든 1억 개든 딱 12KB만 사용한다.
  • 고정된 연산 속도

    • 데이터 양이 많아져도 연산 속도가 느려지지 않는다.
    • O(1)에 가까운 일정한 성능을 보장하므로 실시간 대시보드에 적합하다.
  • 병합의 용이성 (Merging)

    • 오전 방문자 수오후 방문자 수를 합쳐서 전체 하루 방문자 수를 구하는 작업(PFMERGE)이 매우 빠르고 간편하다.
  • 트레이드오프

    • 약간의 오차 (0.81%)
      • 확률로 계산하기 때문에 약 0.81\% 정도의 오차가 발생할 수 있다.
      • 비즈니스 판단
        • 오늘 방문자가 정확히 1,000,001명인가?가 중요하다면 못 쓰지만, 약 100만 명 정도가 왔다는 추세 파악이 목적이라면 매우 좋은의 선택이다.
    • 데이터 조회 불가
      • 누가 왔는지(ID 리스트)는 알 수 없고, 오직 몇 명이나 왔는지(숫자)만 알 수 있다.
      • 데이터를 버리는 대신 숫자를 얻는 방식이기 때문이다.
  • 실무 활용 사례

    • DAU / MAU 집계
      • 하루/한 달 동안의 중복 없는 순 방문자 수 계산.
    • 조회수 중복 방지
      • 동일한 사용자가 게시글을 여러 번 클릭해도 한 번만 카운트하고 싶을 때.
    • 검색어 유입 통계
      • 하루 동안 검색된 유익한 검색어 종류의 개수 파악.

0개의 댓글