Redis에 대해 알아보자

겔로그·2024년 12월 29일

목차

  • 개요
  • Redis
    - Redis란?
    • Redis 특징
      • NoSQL(key-value)
      • In-Memory
    • Redis 아키텍처
      • Single Thread
        • I/O Multiplexing
  • Redis 사용 전략
    • Redis 활용 방안
    • 서버 구성 전략
      • Standalone
      • Replica
      • Sentinel
      • Cluster
    • 아키텍처 선택 기준
  • Redis 명령어
    - 명령어 목록
    • 주의사항 - 사용하면 안되는 커맨드
  • Redis Java client
    • jedis
    • lettuce
  • Redis 응용 라이브러리
    • bucket4j-redis
    • redission

개요

Redis는 단순한 구조를 가지면서도 뛰어난 성능을 제공하는 미들웨어로, 서버 아키텍처 설계에서 중요한 역할을 담당합니다. 최근 시스템에 Redis 도입을 위해 관련 내용을 찾아보았고 이를 정리한 내용입니다.

Redis

Redis란?

Redis는 REmote DIctionary Server의 축약어로 인메모리 NoSQL 키-값(key-value) 저장소입니다. 주로 어플리케이션 캐시와 빠른 응답을 위해 사용되고 있는 미들웨어입니다.

Redis 특징

Redis를 이해하는 핵심 키워드는 다음과 같습니다

  • NoSQL(key-value)
  • in-memory

key-value 구조

Redis는 NoSQL key-value 구조를 채택하고 있습니다.

key는 문자열(String) 구조로 저장되며, 최대 512MB 길이까지 저장이 가능합니다.

value는 다앙한 자료구조를 지원하고 있으며 다음과 같은 목록을 지원합니다.

자료구조 설명
String 단순 키-값 형태의 문자열 데이터
Hash 필드와 값을 가진 Key-Value 형태의 집합
List 순서가 있는 문자열의 리스트 (양방향 삽입/삭제 가능)
Set 중복되지 않는 유니크한 문자열의 집합
Sorted Set 점수와 함께 정렬된 유니크한 문자열 집합
Stream 대규모 데이터 로그 저장 및 처리
Bitmap 비트 단위의 값을 저장할 수 있는 자료구조
HyperLogLog 중복을 허용한 데이터의 고유 개수를 근사치로 계산
Geospatial 위치 기반 데이터를 저장하고 처리할 수 있는 자료구조
JSON JSON 데이터를 Redis에 저장하고 처리할 수 있는 자료구조 (RedisJSON 모듈)

In-Memory

Redis는 모든 데이터를 인메모리 환경에서 관리하고 있습니다. 인메모리 환경에서 데이터 관리시 다음과 같은 특징을 가집니다.

In-Memory 특징

  • 메모리 접근 속도가 디스크 접근 속도보다 빠르기 때문에 데이터 처리 속도에 이점 존재
  • 서버 재시작시 메모리 데이터는 휘발되기 때문에 장애 발생시 데이터 손실 가능성이 존재합니다.

데이터 손실 가능성이 있기 때문에 Redis에서는 Persistence 기능을 지원하고 있습니다.

Persistence 종류

  • AOF(Append Only File): 파일에 사용한 명령어를 로그로 저장한다. 서버 재시작시 파일을 읽어 특정 시점으로 replay 할 수 있다.
  • RDB(Redis Database): 데이터의 특정 시점을 스냅샷하여 관리
  • No persistence: 별도로 persistence 기능을 사용하지 않음
  • RDB + AOF: 한 인스턴스에서 두 가지 모두 관리

보다 더 자세한 내용은 Redis Docs - Redis persistence 참고해주세요.

Redis 아키텍처

Redis 아키텍처는 싱글 스레드와 I/O Multiplexing 방식을 채택하였습니다.

멀티 쓰레드 방식으로 동작하는 것이 속도가 더 빠를 것 같은데 왜 싱글 쓰레드 방식으로 디자인을 했을까요?

Single Thread

Why-isnt-Redis-designed-to-benefit-from-multi-threading에서 해당 의문을 어느정도 정리해봤습니다.

Single Thread로 구현할 때의 이점

  • 구현의 단순함
  • 동시성 이슈 해결

멀티스레스 구현 방식은 동시성 이슈를 해결해야만 하기 때문에 구현 과정에서 복잡성이 높아집니다.

단일 스레드로 동작하면 멀티스레드 환경에서 발생할 수 있는 복잡한 동기화 코드가 필요 없으므로, Redis의 코드가 간단하고 직관적입니다.

멀티스레드를 사용하지 않음으로써, Redis는 내부에서 복잡한 스레드 관리나 데이터 동기화 작업을 하지 않아도 되어, 시스템 자원 소비를 줄이고 성능을 극대화할 수 있습니다.

I/O Multiplexing

Redis는 데이터 처리 부분을 Single Thread로 동작시키지만 여러 클라이언트에서 요청시 네트워크 대기시간을 단축시키기 위해 I/O Multiplexing 기법을 도입했습니다.

I/O Multiplexing 은 단일 스레드가 성능에 영향을 미치지 않고 여러 애플리케이션 요청을 처리할 수 있는 방법입니다. 내용이 조금 복잡해 이해할만한 참고 자료를 공유드리고 소개는 하지 않도록 하겠습니다.

참고

내용을 정리하자면 Redis는 데이터 처리하는 single thread와 클라이언트 요청을 처리하는 multi thread로 이루어져 있음을 알 수 있습니다.

따라서 Redis 자체가 Single Thread로 동작하지 않지만, 데이터 처리는 Single Thread로 동작한다는 것을 알면 좋을 것 같습니다.

별첨

redis 6.0 이후부터는 ThreadedIO 라는 개념이 도입되어 I/O 쪽 처리 방식이 변경되었습니다. 자세한 내용은 [입 개발] Redis 6.0 – ThreadedIO를 알아보자.를 참고 부탁드립니다.

Redis 사용 전략

앞서 Redis의 특징 및 아키텍처를 알아보았습니다. 이제 Redis를 어떻게 사용할지 사용 전략을 알아보겠습니다.

Redis 활용 방안

Redis는 다음과 같은 방식으로 활용 가능합니다.

활용 방안

  • Message Queue
  • Cache
  • Session/Token
  • ETC

일반적으로 분산 환경에서 동일한 데이터를 바라봐야 할 때 사용되며 캐시 용도로 많이 사용되어지는 것 같습니다.

최근 Redis Pub-Sub 기능을 통해 kafka와 같은 메시지 브로커의 역할도 수행할 수 있습니다.

서버 구성 전략

Redis 서버 구성 전략으로는 크게 4가지 방식이 존재합니다.

  • Standalone
  • Replication
  • Sentinel
  • Cluster

Standalone

  • 별도 처리없이 Redis를 단일 서버로 배포해 운영하는 방식
  • 장애 발생시 수동 대응 필요

Replication 구성

  • 일반적인 Master - Slave(Replica) 구조
  • 비동기식 복제
  • HA 기능이 없어 장애 상황시 Slave를 Master 승격 시켜야 함 (수동 대응)

Sentinel

  • Sentinel 노드는 다른 노드를 감시하는 역할
  • 비정상 상태 노드를 확인하여 자동으로 Failover 처리(Master Node 사망시 Slave 승격시키는 노드)
  • 연결정보 필요 없음
  • 3대 이상의 홀수로 구성되어야 하며, 과반수의 Sentinel이 동의해야 Failover 진행

Cluster

  • 스케일 아웃 및 HA 기능 존재
  • 최소 3대의 마스터 노드가 필요
  • 모든 노드가 서로 감시하여, 마스터 노드가 비정상 상태일 경우 자동 Failover
  • 샤딩 기능 지원

아키텍처 선택 기준

Redis 명령어

redis command를 자세하게 알고 싶으신 경우 아래 링크를 참고 부탁드립니다.

참고: Redis Docs - Command

기본 명령어는 다음과 같습니다.

명령어 목록

명령어 설명
SET key value 지정된 키에 값을 저장. 이미 존재하는 키는 값이 덮어쓰기 됨.
GET key 지정된 키의 값을 반환. 값이 없으면 nil을 반환.
DEL key 지정된 키를 삭제. 삭제된 키는 더 이상 존재하지 않음.
EXPIRE key seconds 지정된 키에 만료 시간을 설정. 시간이 지나면 자동으로 삭제됨.
TTL key 지정된 키의 남은 시간(초)을 반환.
INCR key 지정된 키의 값을 1만큼 증가시킴. 키가 없으면 0부터 시작.
DECR key 지정된 키의 값을 1만큼 감소시킴. 키가 없으면 0부터 시작.
LPUSH key value 리스트의 왼쪽에 값을 추가.
RPUSH key value 리스트의 오른쪽에 값을 추가.
LPOP key 리스트의 왼쪽에서 첫 번째 값을 삭제하고 반환.
RPOP key 리스트의 오른쪽에서 첫 번째 값을 삭제하고 반환.
LRANGE key start stop 리스트에서 지정된 범위의 값을 반환. 범위는 0부터 시작.
SADD key member 세트에 값을 추가. 값이 이미 존재하면 무시됨.
SMEMBERS key 세트에 있는 모든 값을 반환.
SREM key member 세트에서 값을 삭제.
HSET key field value 해시에서 특정 필드에 값을 설정.
HGET key field 해시에서 특정 필드의 값을 반환.
HGETALL key 해시에서 모든 필드와 값을 반환.
ZADD key score member 정렬된 세트에 점수와 함께 값을 추가.
ZRANGE key start stop 정렬된 세트에서 지정된 범위의 값을 반환. 범위는 점수를 기준으로 함.
ZREM key member 정렬된 세트에서 값을 삭제.
PING Redis 서버가 응답하는지 확인. 정상적이면 PONG을 반환.
FLUSHDB 현재 데이터베이스의 모든 키를 삭제.
FLUSHALL 모든 데이터베이스의 모든 키를 삭제.
KEYS pattern 지정된 패턴에 맞는 모든 키를 반환. (*와 같은 와일드카드 사용 가능)

주의사항 - 사용하면 안되는 명령어

Redis에는 시간복잡도가 O(1)이 아닌 명령어들이 존재합니다.

Redis는 데이터 처리 로직이 single thread로 동작하기 때문에 데이터가 많아질 수록 소요 시간이 길어지기 때문에 요청에 대한 응답 속도가 매우 지연될 수 있습니다. (timeout 정책으로 인한 장애가 발생할 수도 있음)

시간복잡도 O(N) 명령어 목록

명령어 설명
KEYS pattern 지정된 패턴에 맞는 모든 키를 반환. 패턴에 맞는 키를 모두 확인하므로 O(N) 시간이 소요됨.
SMEMBERS key 세트에 있는 모든 멤버를 반환. 세트의 크기(N)에 비례하는 시간 복잡도.
HGETALL key 해시에서 모든 필드와 값을 반환. 해시의 필드 수(N)에 비례하는 시간 복잡도.
LRANGE key start stop 리스트에서 지정된 범위의 값을 반환. 반환하는 값의 개수(N)에 비례하는 시간 복잡도.
ZREVRANGE key start stop 정렬된 세트에서 지정된 범위의 값을 반환. 반환하는 값의 개수(N)에 비례하는 시간 복잡도.
ZADD key score member 정렬된 세트에 값을 추가할 때, 만약 점수에 따라 순서를 재정렬해야 하면 O(N) 시간이 소요될 수 있음.
HVALS key 해시의 모든 값을 반환. 해시의 크기(N)에 비례하는 시간 복잡도.

이러한 명령어 중 대체 가능한 명령어가 존재하니 만약 필수적으로 사용해야 될 경우 확인 후 사용하시는 것을 권장드립니다.

아래는 대표적인 변경 가능한 명령어 입니다.

  • KEYS -> SCAN
  • HGETALL -> HSCAN
  • DEL -> UNLINK

Redis Java client

redis에서 지원하는 Java Client는 대표적으로 두 가지가 존재합니다.

Jedis

Jedis is a Java client for Redis designed for performance and ease of use.

Lettuce

Lettuce is a scalable thread-safe Redis client for synchronous, asynchronous and reactive usage. Multiple threads may share one connection if they avoid blocking and transactional operations such as BLPOP and MULTI/EXEC. Lettuce is built with netty. Supports advanced Redis features such as Sentinel, Cluster, Pipelining, Auto-Reconnect and Redis data models.

Jedis vs Lettuce (feat. chatgpt)

특징 Jedis Lettuce
기본 클라이언트 유형 블로킹 (Blocking I/O) 비동기 (Asynchronous) / 논블로킹 (Non-blocking I/O)
멀티스레딩 지원 지원 안 함 (단일 스레드로 동작) 지원 (비동기 방식으로 여러 스레드에서 Redis와 상호작용 가능)
응답 처리 동기 방식으로 요청-응답 처리 비동기 방식으로 요청-응답 처리, 스레드 풀 활용 가능
연결 관리 기본적으로 Redis에 대한 각 연결을 새로 생성 (풀링 지원) 커넥션 풀링 지원 (기본적으로 비동기/논블로킹 연결 제공)
스레드 안전성 스레드 안전하지 않음. 각 스레드마다 새로운 클라이언트 인스턴스 필요 스레드 안전성 제공, 여러 스레드에서 공유하여 사용 가능
성능 단일 스레드로 동작하므로 여러 요청을 순차적으로 처리하는 경우 성능 저하 가능 비동기 처리로 높은 동시성 처리 가능, 대량의 요청을 빠르게 처리 가능
사용법 동기적으로 요청을 보내고 응답을 기다리는 전통적인 방식 비동기적, 이벤트 루프를 통해 효율적인 요청 처리 가능
라이브러리 크기 상대적으로 작고 간단함 비동기 기능을 제공하기 때문에 상대적으로 크고 복잡함
Java 지원 Jedis는 Java 8 이상에서 사용 가능 Java 8 이상에서 비동기적 API 사용 가능

내용을 쭉 봤을때 간단한 POC 코드, 샘플 코드를 작성하는것 외에는 lettuce를 사용하는 것이 좀 더 바람직할 것 같네요.

성능적으로나 안정성 쪽으로나 lettuce를 사용하는 것을 권장합니다.

Redis 응용 라이브러리

Redisson

github: Redisson - Valkey & Redis Java client.
Real-Time Data Platform.

Redisson은 분산 환경에서 동기화 로직을 제공하기 위해 Lock Interface를 제공하는 라이브러리입니다. 이 외에도 lettuce에서 제공하지 않는 다양한 고급 설정을 지원하고 있습니다. 자세한 내용은 아래 문서를 참고해주세요.

참고: feature-comparison-redisson-vs-lettuce

bucket4j-redis

github: bucket4j

bucket4j는 rate limit 기능을 구현한 라이브러리입니다.

해당 라이브러리에서는 redis를 활용할 수 있도록 구현해 놓았는데 jedis, lettuce, redisson을 활용하여 rate limit 기능을 제공할 수 있습니다.

별첨

이 외에도 redis는 서비스 환경에서 주의해야 될만한 부분이 있는데요. 좀 더 자세하게 redis를 공부해보고 싶은 분들에게 아래 키워드를 공유드립니다.

  • Redis memory fragmentation (메모리 단편화 이슈)
  • Redis 자료 구조 내부 처리 방식
  • Redis Persistence
  • Cache stempede
  • TTL Issue

이외에도 Redis 공부 시작시 추천드릴만한 영상을 공유드립니다.

Reference

profile
Gelog 나쁜 것만 드려요~

0개의 댓글