Redis는 메모리가 한도에 닿으면 키를 골라 버리거나 쓰기를 막습니다. 그 기준을 정하는 것이 eviction 정책입니다.
메모리가 한계에 닿는 순간
Redis는 작업 데이터를 메모리에 두고, 설정에 따라 디스크에도 영속화합니다. 메모리 한도를 정하는 maxmemory와 한도 초과 시 키를 고르는 eviction 정책을 함께 설정해야 합니다.
maxmemory는 eviction 판단에 쓰는 메모리 한도입니다. 데이터와 관리 오버헤드를 포함한 사용량에서 복제·AOF(Append Only File, 쓰기 로그) 관련 일부 버퍼를 제외해 판단합니다. 따라서 프로세스 전체 메모리는 이 한도를 넘을 수 있습니다.
maxmemory 8gb
maxmemory-policy allkeys-lru
maxmemory-samples 10위 값은 8GiB 캐시를 가정한 설정 예시입니다. maxmemory-policy는 제거 기준을, maxmemory-samples는 근사 LRU/LFU 등의 후보 표본 크기를 정합니다.
eviction이 일어나는 흐름
Redis는 명령 처리 중 메모리 한도를 확인하고, 초과하면 정책에 따라 키를 제거합니다. 제거할 후보가 없거나 정책이 제거를 금지하면 메모리를 늘리는 명령이 실패할 수 있습니다.
flowchart TD
check{"eviction 산정 메모리가<br/>maxmemory 초과?"} -->|아니오| ok["명령 처리"]
check -->|예| policy{"키 제거 허용 정책?"}
policy -->|아니오| reject["메모리 증가 명령 거부"]
policy -->|예| candidate{"제거 후보가 있음?"}
candidate -->|아니오| reject
candidate -->|예| evict["정책에 따라 키 제거"]
evict --> check
마지막 화살표는 다시 점검으로 돌아갑니다. 한 번에 한 키씩 제거하고, 한도 아래로 내려갈 때까지 이 과정을 반복합니다. 그래서 메모리 사용량이 한도에 가까우면 쓰기마다 키 제거가 실행돼 지연이 늘 수 있습니다.
eviction 정책 8가지
정책은 두 축으로 나뉩니다. 대상이 모든 키(allkeys-)인지 만료 시간이 설정된 키(volatile-)인지, 그리고 버리는 기준이 무엇인지입니다. 여기서 만료 시간은 키마다 거는 TTL(Time To Live)을 말합니다.
| 정책 | 대상 | 추천 상황 | 특징 |
|---|---|---|---|
allkeys-lru | 모든 키 | 범용 캐싱 | 최근 미사용 키 우선 제거 |
allkeys-lfu | 모든 키 | 소수 핫 키 집중 접근 | 접근 빈도 낮은 키 우선 제거 |
volatile-lru | TTL 있는 키 | 저수명·장수명 혼재 | TTL 없는 키 보호 |
volatile-lfu | TTL 있는 키 | TTL 붙은 핫 데이터 | 빈도 기반 제거 |
volatile-ttl | TTL 있는 키 | 만료 시간 명시적 | 만료 임박 키 우선 |
volatile-random | TTL 있는 키 | 거의 안 씀 | 임의 선택 |
allkeys-random | 모든 키 | 거의 안 씀 | 임의 선택 |
noeviction | 제거 없음 | 큐·작업 저장소 | 한도 초과 시 쓰기 거부 |
Redis의 기본 정책은 noeviction입니다. 범용 캐시에는 allkeys-lru를 후보로 검토할 수 있고, volatile-*는 TTL이 있는 키만 제거합니다. 후보가 부족하면 쓰기가 실패할 수 있다는 점은 공식 eviction 문서에도 명시돼 있습니다.
근사 LRU와 LFU의 동작
LRU(Least Recently Used)는 가장 오래 안 쓴 키를 버리는 기준입니다. 모든 키를 정렬해 진짜 최하위를 찾으면 비용이 큽니다. 그래서 Redis는 정확한 LRU가 아니라 근사 LRU를 씁니다.
근사 LRU는 전체가 아니라 몇 개를 뽑아 그중 가장 오래된 키를 버립니다. 이 표본 크기를 maxmemory-samples가 정합니다.
maxmemory-samples 10표본을 늘리면 더 오래된 키를 찾을 가능성이 커지지만 CPU 비용도 늘어납니다. 기본값 5와 예시값 10을 실제 접근 패턴에서 비교해 미스율과 지연을 함께 확인합니다.
LFU(Least Frequently Used)는 기준이 다릅니다. 최근성이 아니라 접근 빈도가 낮은 키를 먼저 버립니다. Redis 4.0부터 지원하며, 일부 키에 접근이 쏠리는 파레토 패턴에서 LRU보다 유리합니다.
key1: 1000번 접근
key2: 5번 접근
key3: 8번 접근
LRU → 최근에 안 쓴 순서로 제거 (접근 횟수 무관)
LFU → key2, key3가 먼저 제거될 가능성이 큼 (감쇠·표본에 따라 달라짐)LFU는 키마다 8비트 확률적 로그 카운터로 빈도를 추적합니다. 시간이 지나면 점수를 감쇠시키므로 예전에 인기 있던 키가 영구히 보호되지는 않습니다. 낮은 빈도의 키가 먼저 제거될 가능성은 높지만 순서를 보장하지는 않습니다.
메모리는 데이터보다 크다
실제 메모리 사용량은 저장한 데이터 크기와 같지 않습니다. 할당자 단편화와 각종 버퍼가 더해지기 때문입니다. 이 차이는 INFO memory로 확인합니다.
redis-cli INFO memory아래는 2026-09에 macOS의 libc 기반 Redis 8.10.1 소규모 인스턴스에서 실행한 출력 중 일부입니다.
used_memory:1553968
used_memory_rss:4554752
mem_fragmentation_ratio:2.97
mem_allocator:libc
mem_not_counted_for_evict:0
mem_replication_backlog:0
mem_aof_buffer:0mem_fragmentation_ratio는 RSS를 Redis가 할당한 메모리로 나눈 값입니다. 위 소규모 실측처럼 시작 직후에는 데이터가 적어 비율이 높을 수 있으므로, 1.5 같은 값만으로 심한 단편화라고 단정하지 않습니다. RSS와 할당량의 절대 차이, 할당자 지표를 함께 봅니다.
active defrag는 실행 중 메모리를 재배치해 단편화를 줄입니다. Redis 전용 jemalloc을 포함한 지원 빌드에서 사용할 수 있으며, 아래 활성화 설정은 libc 할당자를 쓰는 빌드에는 적용할 수 없습니다.
activedefrag yes
# 10MB 이하 단편화는 무시
active-defrag-ignore-bytes 10mb
# 단편화 10% 이상이면 실행
active-defrag-threshold-lower 10mem_not_counted_for_evict도 같이 봐야 합니다. 복제·AOF 버퍼처럼 eviction 대상에서 빠지는 메모리를 뜻합니다. 이 값이 크면 used_memory가 한도 아래여도 프로세스 전체는 더 많이 쓰는 셈입니다.
메모리를 줄이는 방법
작은 컬렉션의 압축 인코딩을 유지하면 메모리를 줄일 수 있습니다. Hash와 List는 버전과 크기에 따라 listpack을 사용하며, List가 커지면 여러 listpack 노드를 연결하는 quicklist로 표현합니다.
# 필드 512개 이하면 listpack 유지
hash-max-listpack-entries 512
# 값이 64바이트 이하면 listpack 유지
hash-max-listpack-value 64
# List 노드당 8KiB 제한
list-max-listpack-size -2Hash는 항목 수나 필드명·값 길이가 임계값을 넘으면 hashtable로 바뀝니다. List의 -2는 노드당 8KiB 제한이고, 양수는 바이트가 아닌 항목 수를 뜻합니다. 관련 필드를 작은 Hash에 묶는 효과는 실제 데이터로 MEMORY USAGE를 비교해 확인합니다.
redis_evicted_keys_total은 누적 카운터이므로, 현재 eviction 발생 여부는 일정 구간의 증가량이나 증가율로 판단합니다. 메모리 80%나 단편화 비율 1.5는 알람의 초기 추정값으로만 두고, 평상시 지표와 가용 메모리에 맞춰 조정합니다.
정책 선택 기준
범용 캐시에는 allkeys-lru를 먼저 평가할 수 있습니다. 접근 빈도가 장기간 유지되는 핫 키가 뚜렷하면 allkeys-lfu와 미스율을 비교합니다. 데이터 손실을 허용할 수 있는지와 실제 접근 분포를 기준으로 선택합니다.
volatile-*는 TTL 있는 키만 버리고 싶을 때만 고릅니다. 캐시와 영구성 데이터가 한 인스턴스에 섞여 있고, TTL 없는 키를 절대 잃으면 안 될 때가 그 경우입니다. TTL이 있는 키가 부족하면 제거할 후보가 없어 쓰기가 막힐 수 있습니다. 따라서 TTL 설정 비율을 함께 확인합니다.
noeviction은 데이터를 버리면 안 되는 큐나 작업 저장소에 씁니다. 한도를 넘으면 키를 지우는 대신 쓰기를 거부합니다. 애플리케이션은 이 오류를 처리해야 합니다.
정리
maxmemory는 eviction 산정 메모리를 제한하고, 정책은 어떤 키를 제거할지 정합니다. 캐시의 실제 접근 패턴으로 LRU와 LFU를 비교하고, 제거가 불가능할 때의 쓰기 오류도 처리해야 합니다.
프로세스 메모리에는 오버헤드와 버퍼도 포함되므로 RSS와 할당자 지표를 함께 봅니다. listpack과 active defrag 설정은 실제 인코딩과 할당자 지원 여부를 확인한 뒤 적용합니다.