Redis 1편 - Redis란?

Redis 1편 - Redis란?

Redis는 인메모리 기반의 키-값 데이터베이스로, 빠른 응답 속도와 다양한 자료구조를 지원한다. 분산 캐시와 메세지 브로커로 활용되며, 영속화 기능도 제공한다.

Redis는 뭘까?

Redis (/ˈrɛdɪs/;[8][9] Remote Dictionary Server)[8] is an in-memory key–value database, used as a distributed cache and message broker, with optional durability.[10] Because it holds all data in memory and because of its design, Redis offers low-latency reads and writes, making it particularly suitable for use cases that require a cache. Redis is the most popular NoSQL database,[11][12][13] and one of the most popular databases overall.[14]

번역해보면 인메모리 기반의 키-값 데이터베이스라고 한다. 분산 캐시메세지브로커로 활용되고 선택적으로 디스크에 영속화도 지원해준다. 빠른 입출력을 위해 모든 데이터를 메모리에 저장해두기 때문에 반응이 빠르다. 이때문에 가장 인기있는 NoSQL이라고 한다.

오케이. 좋다는건 누구나 알고 있다. 결국은 쓰기 위해 만들어진 것. Redis에 대한 특징을 알아보고 그 특징 별로 사용법을 배워보자. 그리고 구체적인 사용 코드와 테스트를 통해 학습하려고 한다.

Redis에 대한 특징

  • 우리 Redis는요? 인메모리 기반 이라서요…

    • 모든 데이터를 메모리에 저장하고 읽고 써요. 그래서 디스크 I/O 접근보다 매우 빨라서 응답 대기 시간(Latency) 도 짧답니다.
    • 실제 인메모리 캐시는 마이크로초 단위의 짧은 지연으로 초당 수백만 건의 처리량을 감당할 수도 있고, 대용량 트래픽에서도 DB 부하를 줄이고 사용자에게 즉각적인 응답을 주는데 핵심적인 역할을 해요.
  • 그리고 키-값 NoSQL 인데요…

    • 다양한 자료구조를 지원하는 유연성을 가지고 있어요.
    • String : 단순 문자열
      • O(1) 읽기/쓰기 성능
      • TTL 설정으로 손쉬운 만료 관리
    • List : 삽입 순서를 보장하는 리스트 형태
    • Set : 중복 없는 집합 데이터
    • Hash : 필드와 값의 매핑 형태로 객체 표현에 적절
    • Sorted Set : 멤버와 점수(score)를 함께 저장해서 점수 기반으로 자동 정렬되는 집합으로 ‘실시간 랭킹’, ‘리더보드’ 구현에 최적화
    • HyperLogLogs : 엄청 크고 유니크한 값 카운팅 할 때 사용할 수 있는 것들이다. 카운팅에 저장된 데이터는 확인할 수 없지만, 매우 적은 용량으로 큰 분량의 카운팅이 가능하다.
    • Streams : 로그나 메세징을 위한 자료구조로 append-only라 중간에 바뀌지 않는다. 시간 범위로 가져온다.
  • 분산 캐시 의 특징으로는요

    • 분산환경에서 캐시의 고가용성과 확장성을 제공해줘요.

    • 복제(Replication) : 하나의 Redis 인스턴스(마스터)의 데이터를 여러 슬레이브(읽기 전용 복제본)에 실시간으로 복제하여 읽기 부하를 분산해요. 마스터가 장애나면 슬레이브를 마스터로 승격시키는 센티넬(Sentinel) 구조도 지원됩니다.

    • 클러스터링(Clustering) : 완전한 대규모 확장을 위해 Redis Cluster 모드를 채택할 수 있어요. 키를 여러 노드에 샤딩(Sharding) 하여 저장함으로써 수평 확장고가용성을 구현할 수도 있습니다. 내부적으로 16384개의 해시 슬롯을 사용하여 키를 노드에 분배하여 데이터가 고르게 퍼지도록 해요.

    • 전역 캐시(Global Cache) : 여러 애플리케이션 서버 인스턴스가 동일한 Redis 캐시를 공유하니까 데이터 일관성을 유지하기가 쉬워요. 그래서 전역적으로 관리하는 세션 데이터를 저장하면 로드밸런싱 환경에서도 어느 서버로 가던지 안정적인 세션 관리가 가능해집니다.

    • 계층적 캐시 : 모든 캐시를 Redis인 전역캐시에 의존하기보다, 지역 캐시와 함께 계층적으로 조합해 활용하면, Hot Key 문제 같이 특정 노드에 대한 과부하를 완화하고 대비할 수 있습니다. 1차적으로 로컬캐시에서 응답하고 없으면 2차 캐시, 3차적으로 DB 네트워킹을 통해 진행하는것입니다. 로컬캐시는 네트워킹도 필요없기 때문에 가장 빠르지만, 아무래도 로컬캐시 데이터 간의 싱크 문제를 신경은 꼭 써야합니다.

      • 지역캐시는 Application 내부 메모리에 유지되는 캐시로 EhCache, Guava Cache, Caffeine Cachce가 있습니다. 전역캐시는 별도의 캐시 서버에 캐시를 저장하는 건데 Redis라고 할 수 있죠.

  • 메세지 브로커 특징

    • 메시지 브로커 특징 (Message Broker Feature) Redis는 Publish/Subscribe (발행/구독) 메시징 기능을 기본적으로 제공하여, 간단한 실시간 메시지 브로커로 활용될 수 있습니다.

      작동 방식: 한 쪽에서 특정 채널(channel)에 메시지를 발행(PUBLISH)하면, 해당 채널을 구독(SUBSCRIBE) 중인 모든 클라이언트가 실시간으로 메시지를 받아 처리하는 구조입니다.

      활용 예시: 채팅 시스템, 실시간 알림 브로드캐스트, 또는 캐시 무효화 통지(Cache Invalidation Broadcast)와 같은 시나리오에 적합합니다. 예를 들어, 다중 서버 환경에서 특정 데이터가 변경되면 Redis 채널을 통해 “invalidate [키]” 메시지를 보내 모든 서버의 로컬 캐시를 지우는 방식으로 분산 캐시 일관성 문제를 완화할 수 있습니다.

      제약 사항: Redis Pub/Sub은 메시지를 브로드캐스트하지만, 내구성 있는 큐(queue)는 아닙니다. 즉, 구독자가 없을 때 발행된 메시지는 유실되며, 메시지 소비에 대한 확인(ACK) 개념이 없습니다. 따라서 지속성이 필요하거나 메시지 손실이 허용되지 않는 중요한 업무 이벤트 스트림에는 Kafka나 RabbitMQ와 같은 전문 메시지 브로커가 더 적합합니다.

  • 영속화 특징

    • Redis는 기본적으로 인메모리 데이터베이스이지만, 데이터 손실을 방지하고 재시작 시 데이터를 복구할 수 있도록 영속성(Persistence) 기능을 제공합니다. 주요 영속화 방식은 다음과 같습니다:

      RDB (Redis Database) 스냅샷: 특정 시점의 Redis 메모리 데이터를 디스크에 스냅샷 파일(.rdb)로 덤프하는 방식입니다. 이는 재시작 시 빠르게 데이터를 로드할 수 있게 해주며, 주기적인 백업 용도로 활용됩니다.

      AOF (Append-Only File): Redis에 대한 모든 쓰기 명령을 로그 파일에 순차적으로 기록하는 방식입니다. 이 방식은 RDB보다 데이터 안정성이 높지만, 모든 쓰기 연산을 디스크에 동기식으로 기록할 경우 디스크 I/O 속도가 전체 성능의 병목이 될 수 있습니다. AOF 파일이 커지면 리라이트(Rewrite) 과정에서 CPU와 디스크 부하가 발생할 수 있습니다.

      혼합 지속성: 운영 환경에서는 RDB와 AOF를 혼합하여 사용하는 것을 권장합니다. 예를 들어, 15분마다 RDB 스냅샷을 뜨고, 그 사이의 변경 사항은 AOF(매 1초마다 동기화, everysec)로 기록하면, 대부분의 복구는 RDB로 빠르게 하고 최근 1초 이내 데이터만 AOF로 적용하여 지속성과 성능 간의 균형을 맞출 수 있습니다.

      AOF 병목 완화: AOF 사용 시 디스크 병목이 발생한다면 appendfsync 옵션을 everysec로 조정하여 동기화 주기를 늘리거나, 고속 SSD 사용, Linux Transparent Huge Pages (THP) 비활성화 등의 튜닝을 고려할 수 있습니다. 극단적인 경우, 캐시 용도로 사용하며 데이터 유실을 감수할 수 있는 시나리오에서는 AOF를 아예 비활성화하고 RDB만 사용하거나 지속성 자체를 끄는 것도 가능합니다.

응답 속도의 균일성이 조금 떨어질 수 있나요?

Redis의 메모리 할당 구조는 jemalloc을 사용합니다. 그래서 매 번 malloc과 free를 통해서 메모리 할당이 됩니다.
이 과정에서 메모리 딘편화(fragmentation)이 발생하여 메모리 할당에 필요한 비용때문에 응답속도가 느려질 수 있습니다.
그러나, 이는 아주 극단적이고 크리티컬한 상황에서 발생할 가능성의 문제고 slab 할당 구조를 가진 Memcached에 비해 균일하지 못한 평가입니다.
큰 규모의 서비스에서도 Redis가 사용되고 있기 때문에, 실제로는 큰 문제가 되지 않는다고 판단하고 있습니다.

메모리 단편화와 jemalloc에 대해

메모리 단편화(fragmentation) 는 공간이 충분히 있으면서도 작은 조각으로 나뉘어져 있어 할당이 불가능한 상황을 말합니다.

구글의 tcmalloc과 함께 가장 많이 사용되는 메모리 할당자입니다. 메모리 할당/해제를 많이 하는 프로그램에서는 메모리 공간 활용과 정리가 속도에 유효한 영향을 미칩니다. je나 tc malloc 메모리 할당자를 사용하는 것 만으로 그 이전과 10~20% 정도 성능 향상을 기대할 수 있을 정도입니다.