Redis는 고성능의 오픈 소스 인메모리 NoSQL 데이터베이스입니다. C 언어로 개발되었으며, 메모리 기반 동작으로 인해 기존 디스크 기반 데이터베이스보다 훨씬 빠른 데이터 읽기 및 쓰기 속도를 제공합니다. 불필요한 임시 데이터의 저장 및 관리를 피함으로써 애플리케이션의 전반적인 성능을 크게 향상시킬 수 있습니다.
1. Redis 개요 및 특징
Redis는 키(Key)와 다섯 가지 종류의 값(Value) 유형(문자열, 리스트, 세트, 해시, 정렬된 세트) 간의 매핑을 저장할 수 있습니다. 또한, 복제(replication), Lua 스크립팅, LRU(Least Recently Used) 기반의 캐시 제거, 트랜잭션 및 다양한 수준의 디스크 영속성(persistence) 기능을 내장하고 있습니다. Redis Sentinel과 Cluster를 통해 고가용성(High Availability)을 제공하여, 수백 기가바이트의 데이터를 저장하고 초당 수백만 건의 요청을 처리할 수 있는 시스템으로 쉽게 확장할 수 있습니다.
주요 특징:
- 고유한 데이터 구조: 다른 데이터베이스에서는 찾아보기 힘든 다양한 고급 데이터 구조를 제공합니다.
- 인메모리 스토리지: 데이터를 메모리에 저장하여 매우 빠른 처리 속도를 보장합니다.
- 원격 접근성: 여러 클라이언트 및 서버가 동시에 연결하여 데이터를 공유할 수 있습니다.
- 영속성: 서버 재시작 후에도 이전 데이터를 유지할 수 있도록 다양한 영속화 옵션을 제공합니다.
- 확장성: 마스터-슬레이브 복제 및 데이터 샤딩을 통해 시스템을 유연하게 확장할 수 있습니다.
2. Redis의 단일 스레드 모델
Redis는 단일 스레드 모델을 채택하고 있지만, 놀라운 성능을 발휘합니다. 그 이유는 다음과 같습니다.
- 메모리 기반 작업: Redis의 데이터는 주로 메모리에 저장되어 HashMap과 같이 빠른 O(1) 시간 복잡도로 데이터를 조회하고 조작할 수 있습니다.
- CPU 비병목화: Redis의 성능 병목은 주로 메모리 크기와 네트워크 대역폭에 있으며, CPU는 병목이 되는 경우가 드뭅니다. 단일 스레드 모델은 다중 스레드 간의 문맥 전환(context switching) 및 경쟁 조건(race condition)을 방지하여 성능 오버헤드를 줄이고 데드락 문제를 회피합니다.
- 효율적인 데이터 구조 설계: Redis의 데이터 구조는 효율적인 작업을 위해 설계되어, 다중 스레드 환경에서 발생할 수 있는 잠금(locking) 및 잠금 해제(unlocking) 작업 없이도 높은 성능을 유지할 수 있습니다.
- I/O 멀티플렉싱 기술: Redis는
select/epoll과 같은 I/O 멀티플렉싱 기술을 활용하여 여러 클라이언트 요청을 동시에 처리합니다. 이는 각 요청마다 새로운 스레드를 생성할 필요 없이 효율적인 동시성을 가능하게 합니다.
3. Java Redis 클라이언트
Java 애플리케이션에서 Redis를 사용하기 위한 대표적인 클라이언트는 다음과 같습니다.
- Jedis: Redis 명령어를 추상화하여 제공하는 경량 Java 클라이언트입니다. 연결 풀 관리를 지원하며, 종종 비즈니스 로직에 맞춰 래핑하여 사용됩니다.
- Redisson: 분산 객체와 서비스를 제공하는 고급 Redis 클라이언트입니다. 더 풍부한 기능을 제공하며, 분산 잠금, 큐, 맵 등을 쉽게 구현할 수 있습니다.
4. Redis 기본 설정
- 기본 데이터베이스: Redis는 총 16개의 데이터베이스를 제공하며, 별도로 지정하지 않으면 0번 데이터베이스에 연결됩니다. 각 데이터베이스는 데이터를 공유하지 않습니다.
- 키-값 최대 크기: Redis의 키와 값은 최대 512MB까지 저장할 수 있습니다. 일반적으로 키의 길이가 10KB를 초과하면 '빅 키(Big Key)'로 분류되기도 합니다.
5. Redis 활용 시나리오
Redis는 다양한 현대 애플리케이션에서 광범위하게 사용됩니다.
- 캐싱 서비스: 자주 접근하는 '핫 데이터'를 캐시하여 데이터베이스에 대한 부하를 줄이고 응답 속도를 향상시킵니다. 캐시 미스 시 데이터베이스 조회를 수행하여 캐시 누락(cache penetration)을 방지해야 합니다.
- 분산 잠금: 분산 시스템 환경에서 공유 자원에 대한 동시 접근을 제어하기 위한 효율적인 분산 잠금 메커니즘으로 활용됩니다.
- 카운터 및 통계: 투표 시스템, 실시간 랭킹, 사용자 활동 집계 등 다양한 종류의 카운터 및 통계 데이터를 효율적으로 처리하고 저장합니다.
- 웹 애플리케이션 지원:
- 사용자 세션 관리 (로그인 토큰, 세션 공유)
- 장바구니 구현 (각 사용자 장바구니를 해시로 저장)
- 동적 웹 페이지 캐싱
- 데이터베이스 행 캐싱
- 웹 페이지 방문 기록 분석
- 발행/구독(Pub/Sub) 시스템: 메시지 브로커 역할을 하여 발행자가 메시지를 발행하면 구독자들이 이를 수신하는 메시징 패턴을 구현합니다.
- 로그 기록: List 데이터 구조를 활용하여 로그 데이터를 저장하고 관리합니다. 새로운 로그는 리스트의 끝에 추가됩니다.
- 지리 공간(Geo-spatial) 서비스:
- IP 기반 위치 조회 (국가, 도시 정보)
- 지오펜싱 (특정 지역 진입/이탈 감지)
- '주변 사람/장소 찾기' 기능 구현 (예: 데이팅 앱, 지도 서비스)
- 배송 서비스에서 최적 배송원 또는 창고 찾기
- 서비스 검색 및 설정 관리: 서버 유지보수 플래그, 다양한 애플리케이션 구성 정보 등을 Redis에 저장하여 서비스 디스커버리와 동적 설정 관리를 지원합니다.
- 애플리케이션 컴포넌트 구축:
- 접두사 매칭을 통한 자동 완성 기능
- 동시성 제어를 위한 카운팅 세마포어
- 비동기 작업을 위한 작업 큐
- 파일 분배 시스템
- 검색 기반 애플리케이션: 검색 결과 정렬, 광고 타겟팅, 채용 정보 검색 등 복잡한 검색 기능을 구현하는 데 활용됩니다.
6. Redis의 8가지 메모리 회수 정책
Redis는 인메모리 데이터베이스이므로, 할당된 최대 메모리 용량에 도달했을 때 새로운 데이터를 저장하려면 기존 데이터를 제거해야 합니다. 이때 Redis는 설정된 메모리 회수(Eviction) 정책에 따라 데이터를 제거합니다.
메모리 회수 정책 목록:
noeviction(기본값): 메모리가 부족해도 데이터를 제거하지 않습니다. 모든 쓰기 작업(write operations)을 거부하고 클라이언트에게 "OOM command not allowed when used memory" 오류를 반환합니다. 읽기 및 삭제 작업만 허용됩니다.allkeys-lru: 전체 키(all keys) 중에서 LRU(Least Recently Used, 가장 오랫동안 사용되지 않은) 알고리즘을 사용하여 데이터를 제거합니다.allkeys-lfu: 전체 키 중에서 LFU(Least Frequently Used, 가장 적게 사용된) 알고리즘을 사용하여 데이터를 제거합니다. (Redis 4.0에서 추가)volatile-lru: 만료 시간(TTL)이 설정된 키(volatile keys) 중에서 LRU 알고리즘을 사용하여 데이터를 제거합니다.volatile-lfu: 만료 시간이 설정된 키 중에서 LFU 알고리즘을 사용하여 데이터를 제거합니다.allkeys-random: 전체 키 중에서 무작위로 데이터를 제거합니다.volatile-random: 만료 시간이 설정된 키 중에서 무작위로 데이터를 제거합니다.volatile-ttl: 만료 시간이 설정된 키 중에서 남은 만료 기간이 가장 짧은 데이터를 우선적으로 제거합니다.
참고: volatile-lru, volatile-lfu, volatile-random, volatile-ttl 정책을 사용할 때, 제거할 수 있는 만료 시간이 설정된 키가 없다면 noeviction과 동일하게 오류를 반환합니다.
적절한 회수 정책 선택:
INFO 명령을 통해 캐시 히트(keyspace_hits) 및 미스(keyspace_misses) 통계를 확인하고 캐시 적중률 (keyspace_hits / (keyspace_hits + keyspace_misses))을 모니터링하여 최적의 정책을 선택할 수 있습니다.
allkeys-lru정책: 일부 데이터가 다른 데이터보다 훨씬 자주 접근되는 경우 (예: 20%의 데이터가 80%의 사용량을 차지하는 멱법칙 분포), 또는 어떤 정책을 사용할지 확실하지 않을 때 좋은 선택입니다.allkeys-random정책: 모든 키가 균등하게 또는 순환적으로 접근되는 시나리오에 적합합니다.volatile-ttl정책: 대부분의 캐시가 서로 다른 TTL 값을 가지고 있고, Redis에게 만료 예정인 키를 우선적으로 제거하도록 힌트를 주고 싶을 때 유용합니다.
팁: 키에 만료 시간을 설정하는 것은 추가적인 메모리를 차지합니다. 따라서 allkeys-lru와 같은 정책은 메모리 압박 상황에서 만료 설정 없이도 키를 회수할 수 있으므로 메모리 효율적일 수 있습니다.
메모리 회수 정책 설정 및 조회:
현재 메모리 회수 정책 조회:
127.0.0.1:6379> CONFIG GET maxmemory-policy
Redis의 최대 사용 가능 메모리 크기 조회:
127.0.0.1:6379> CONFIG GET maxmemory
설명: maxmemory를 설정하지 않거나 0으로 설정하면, 64비트 운영체제에서는 Redis가 사용 가능한 모든 메모리를 사용합니다. 32비트 운영체제에서는 최대 3GB까지 사용할 수 있습니다 (시스템 운영에 필요한 자원 때문에 4GB 중 일부만 사용 가능).
메모리 회수 정책 설정 (Redis 설정 파일 redis.conf 수정):
maxmemory-policy allkeys-lru
메모리 회수 정책 설정 (명령어로 임시 적용, 재시작 시 초기화):
127.0.0.1:6379> CONFIG SET maxmemory-policy allkeys-lru
Redis 최대 사용 메모리 크기 설정 (명령어로 임시 적용, 재시작 시 초기화):
예를 들어, 최대 100MB로 설정:
127.0.0.1:6379> CONFIG SET maxmemory 100mb