Redis OOM 에러(used memory > maxmemory) 5분 응급처치 완벽 가이드
배포도 안 했는데 갑자기 운영 로그에 이런 메시지가 도배되기 시작합니다.
(error) OOM command not allowed when used memory > 'maxmemory'.읽기는 되는데 SET, INCR, LPUSH 같은 쓰기 명령만 전부 거부되는 상황. 세션 저장소나 캐시로 Redis를 쓰고 있다면 그대로 서비스 장애로 직결됩니다. 이 글은 "지금 장애 중"인 분을 위해 응급 처치를 앞에, 원인 분석을 뒤에 배치했습니다. 아래 로드맵대로 따라오면 5분 안에 쓰기를 복구할 수 있습니다.
[1] INFO memory 로 현황 파악 (30초)
[2] maxmemory 늘리거나 / eviction 정책 바꿔 즉시 복구 (1분)
[3] CONFIG REWRITE 로 영구 저장 (10초)
[4] TTL · 모니터링으로 재발 방지 (이후)1. 30초 현황 파악: INFO memory 읽는 법
가장 먼저 지금 Redis가 어떤 상태인지 봐야 합니다.
redis-cli INFO memory# Memory
used_memory:2147483648
used_memory_human:2.00G
used_memory_rss:2415919104
used_memory_rss_human:2.25G
maxmemory:2147483648
maxmemory_human:2.00G
maxmemory_policy:noeviction
mem_fragmentation_ratio:1.12
mem_allocator:jemalloc-5.3.0핵심 지표 4개만 봅니다.
| 지표 | 의미 | 판별 기준 |
|---|---|---|
used_memory | Redis가 논리적으로 쓰는 메모리 | maxmemory에 근접/도달하면 위험 |
used_memory_rss | OS가 실제로 점유한 물리 메모리(RSS) | used_memory보다 과도하게 크면 단편화 |
mem_fragmentation_ratio | rss ÷ used_memory | 1.0~1.4 정상 / 1.5↑ 단편화 / 1.0 미만 스왑 의심 |
maxmemory_policy | 한계 도달 시 동작 | noeviction이면 쓰기 거부의 직접 원인 |
위 예시는 used_memory == maxmemory이고 정책이 noeviction입니다. 즉, 메모리가 꽉 찼고 비울 정책도 없으니 쓰기를 거부하는 전형적 상황입니다.
보조 명령도 알아두면 좋습니다.
redis-cli MEMORY DOCTOR # Redis가 직접 진단 코멘트를 줌
redis-cli MEMORY USAGE mykey # 특정 키가 차지하는 바이트2. 즉시 복구: maxmemory 조정과 정책 선택
상황은 둘 중 하나입니다. (A) 메모리를 더 줄 수 있다 또는 (B) 캐시라서 일부 버려도 된다.
(A) 메모리 여유가 있으면 한계만 올린다
서버 RAM에 여유가 있다면 maxmemory를 무중단으로 늘립니다.
redis-cli CONFIG SET maxmemory 4gb이 명령은 즉시 적용되고, 직후부터 쓰기가 다시 동작합니다.
(B) 순수 캐시라면 eviction 정책을 켠다
캐시 용도인데 noeviction으로 운영했다면 이게 근본 문제입니다. LRU 기반으로 오래된 키를 자동으로 밀어내게 합니다.
redis-cli CONFIG SET maxmemory-policy allkeys-lru정책을 바꾸는 순간 한계 초과분이 evict되며 쓰기가 즉시 복구됩니다.
maxmemory-policy 8종 비교표
상황에 맞는 정책을 고르는 것이 핵심입니다.
| 정책 | 동작 | 대상 키 | 캐시용 | 세션·영속용 |
|---|---|---|---|---|
noeviction | 한계 도달 시 쓰기 거부(에러) | - | ✕ | ◎(증설 전제) |
allkeys-lru | 가장 오래 안 쓴 키 제거 | 전체 | ◎ | △ |
allkeys-lfu | 가장 적게 쓰인 키 제거 | 전체 | ◎(추천 증가) | △ |
volatile-lru | TTL 있는 키 중 LRU | TTL 키만 | ○ | ○ |
volatile-lfu | TTL 있는 키 중 LFU | TTL 키만 | ○ | ○ |
allkeys-random | 무작위 제거 | 전체 | △ | ✕ |
volatile-random | TTL 키 중 무작위 | TTL 키만 | △ | △ |
volatile-ttl | 만료 임박 키 우선 제거 | TTL 키만 | ○ | ◎(TTL 혼재) |
선택 가이드
- 순수 캐시:
allkeys-lru또는 접근 빈도 편차가 큰 경우allkeys-lfu - TTL 키와 영속 키가 섞인 환경:
volatile-ttl또는volatile-lru(TTL 없는 키는 보호됨) - 데이터 유실이 절대 불가(세션·큐·영속 저장):
noeviction유지 + 메모리 증설이 정답. 정책으로 버티려 하지 마세요.
영구 저장
CONFIG SET은 재시작하면 사라집니다. 반드시 설정 파일에 반영합니다.
redis-cli CONFIG REWRITE또는 redis.conf를 직접 수정해 둡니다.
maxmemory 2gb
maxmemory-policy allkeys-lru
maxmemory-samples 5maxmemory-samples는 LRU/LFU가 제거 후보를 고를 때 표본 수입니다. 기본 5면 충분하고, 정밀도를 높이려면 10까지 올릴 수 있지만 CPU를 더 씁니다.
3. 원인 진단: OOM을 부르는 5가지 시나리오
복구했다면 이제 왜 터졌는지 봅니다. 증상→원인→확인 순서로 정리합니다.
① maxmemory 한계 도달
- 증상:
used_memory ≈ maxmemory, 쓰기 전면 거부 - 원인: 데이터 증가량이 할당 메모리를 초과
- 확인:
INFO memory에서 두 값 비교
② 정책이 noeviction
- 증상: 캐시인데도 키를 안 버리고 에러만 냄
- 원인: 기본값
noeviction을 캐시에 그대로 사용 - 확인:
CONFIG GET maxmemory-policy
③ 메모리 단편화로 실질 부족
- 증상: used_memory는 여유 있는데 OOM 또는 OS 스왑 발생
- 원인: 잦은 키 생성/삭제로 jemalloc 단편화, RSS가 부풀어 OS 메모리 압박
- 확인:
mem_fragmentation_ratio1.5 이상
④ RDB/AOF rewrite의 fork(COW) 압박
- 증상:
BGSAVE/AOF rewrite 시점에 메모리 급증, fork 실패 로그 - 원인: fork 시 Copy-On-Write로 쓰기가 많으면 부모 메모리가 복제됨. 최악엔 2배까지 순간 점유
- 확인: 로그의
Can't save in background: fork: Cannot allocate memory,INFO persistence의rdb_last_bgsave_status
⑤ 키에 TTL 미설정으로 무한 누적
- 증상: 시간이 갈수록 used_memory가 단조 증가
- 원인: 캐시인데 만료를 안 걸어 영원히 살아있는 키
- 확인:
redis-cli --bigkeys,DBSIZE추세 관찰
실무 경험담: 제가 겪은 OOM의 절반 이상은 ⑤번, TTL 누락이었습니다. 코드 리뷰에서 "이 SET에 EX 빠졌어요" 한 줄이면 막을 일을, 몇 달 뒤 새벽 장애로 되갚는 경우가 많습니다. 정책 튜닝보다 TTL 컨벤션 강제가 비용 대비 효과가 가장 큽니다.
4. 재발 방지 체크리스트
캐시 키엔 무조건 TTL을 건다. 쓰기 시점에 만료를 함께 지정하세요.
SET session:1234 "payload" EX 3600 # 1시간
SETEX cache:user:99 600 "..." # 10분단편화 대응. Redis 4.0+의 활성 디프래그를 켭니다.
redis-cli CONFIG SET activedefrag yes단편화가 심해 RSS가 안 빠지면 마지막 수단으로 재시작 시 RSS가 회수됩니다(단, 데이터 영속화 후 진행).
모니터링 알람. redis_exporter + Prometheus + Grafana로 메모리 사용률 80%에서 알람을 겁니다.
groups:
- name: redis-memory
rules:
- alert: RedisMemoryHigh
expr: redis_memory_used_bytes / redis_memory_max_bytes > 0.8
for: 5m
labels:
severity: warning
annotations:
summary: "Redis used_memory가 maxmemory의 80%를 초과했습니다"이 알람 하나면 한계 도달 전에 증설·정리 시간을 벌 수 있습니다.
용량 산정 팁. 운영 데이터 기준 used_memory_rss에 fork/COW 여유로 1.3~1.5배를 곱해 maxmemory와 서버 RAM을 잡으세요. RDB를 쓴다면 더 보수적으로.
Redis 7.x에서는
maxmemory-clients로 클라이언트 버퍼까지 제한할 수 있고, 빈도 기반allkeys-lfu채택이 늘고 있습니다. 참고로 2024~2025년 라이선스 변경(RSAL→AGPL) 이후 Valkey로 옮기는 흐름도 있지만, maxmemory 동작과 명령은 동일하니 이 가이드를 그대로 적용할 수 있습니다.
자주 묻는 질문 (FAQ)
Q. CONFIG SET maxmemory-policy를 바꾸면 데이터가 즉시 삭제되나요?
A. 정책을 evict 계열로 바꾸고 used_memory가 maxmemory를 초과한 상태라면, 초과분만큼 키가 즉시 제거됩니다. TTL 없는 영속 데이터가 있다면 allkeys-* 대신 volatile-*를 쓰거나 maxmemory를 먼저 늘리세요.
Q. CONFIG SET으로 바꿨는데 재시작하면 원래대로 돌아갑니다.
A. CONFIG SET은 메모리상 설정만 바꿉니다. redis-cli CONFIG REWRITE로 redis.conf에 반영하거나, 설정 파일을 직접 수정해야 재시작 후에도 유지됩니다.
Q. used_memory는 여유가 있는데 OOM이 납니다. 왜죠?
A. mem_fragmentation_ratio를 확인하세요. 1.5 이상이면 단편화로 RSS가 부풀어 OS 메모리가 부족한 상태입니다. activedefrag yes를 켜거나, 영속화 후 재시작으로 RSS를 회수하면 해결됩니다.
이 글은 AI 에이전트가 1차 초안을 작성한 뒤, 사람 편집자가 사실관계·출처·톤과 맥락을 검토하여 발행했습니다. 오류나 부정확한 내용이 확인되면 24시간 이내에 정정합니다.
댓글
불러오는 중...