Redis OOM command not allowed: `maxmemory`와 eviction 후보부터 보자
Dev
마지막 업데이트

Redis OOM command not allowed: `maxmemory`와 eviction 후보부터 보자


Redis가 OOM command not allowed when used memory > 'maxmemory'를 반환하면, 많은 팀이 곧바로 “서버 RAM이 완전히 바닥났다”고 해석합니다. 실제로 한 서비스에서 Redis OOM이 터졌을 때, 호스트 RAM은 8GB 중 3GB나 남아 있었습니다. 문제는 maxmemory가 2GB로 설정되어 있고 volatile-lru 정책인데, 대부분의 key에 TTL을 안 걸어서 eviction 후보가 거의 없었던 것이었습니다. TTL 정책만 바로잡으니 OOM이 사라졌습니다. 하지만 실제로 Redis가 말하는 건 더 좁은 경우가 많습니다. 현재 메모리 정책 기준으로는 이 memory-growing command를 더 이상 받아들일 수 없다는 뜻입니다.

짧게 말하면 먼저 maxmemory, maxmemory-policy, 그리고 실제 eviction candidate 집합을 확인해야 합니다. 대부분의 incident는 hardware 문제라기보다 policy와 workload shape 문제에 더 가깝습니다.


이 글이 맞는 상황

아래 중 하나면 여기서 시작하면 됩니다.

  • host에는 RAM이 남아 보이는데 Redis는 OOM으로 write를 거절한다
  • cache write는 실패하는데 read는 계속 된다
  • 팀은 eviction을 기대했는데 Redis가 command를 거절한다
  • volatile-* policy를 썼는데도 memory-growing write가 계속 실패한다
  • 일부 key 또는 특정 write path에서만 incident가 갑자기 터진다

처음 10분에 볼 것

1차 확인은 이 정도면 충분합니다.

redis-cli INFO memory
redis-cli INFO stats
redis-cli CONFIG GET maxmemory
redis-cli CONFIG GET maxmemory-policy
redis-cli --bigkeys

수상한 key family가 보이면 여기에 더하세요.

redis-cli MEMORY USAGE my:key SAMPLES 0
redis-cli TTL my:key

이 단계에서 답해야 할 질문은 네 개입니다.

  • Redis가 configured maxmemory ceiling에 실제로 닿았는가
  • pressure 시점에 어떤 policy가 활성화되어 있는가
  • Redis가 실제로 eviction할 수 있는 후보 key를 가지고 있는가
  • big key, missing TTL, persistence buffer가 incident를 키우고 있는가

이 OOM 메시지가 실제로 뜻하는 것

Redis eviction 문서는 cache가 maxmemory를 넘으면 선택된 eviction policy를 강제한다고 설명합니다. noeviction에서는 Redis가 key를 지우지 않고 새 데이터를 추가하는 command에 대해 error를 반환합니다.

즉 이 OOM 메시지는 보통 아래 의미입니다.

  • Redis policy boundary 기준으로 memory pressure가 실제로 존재한다
  • 그 command는 추가 메모리가 필요하다
  • 현재 policy로는 적절한 key를 충분히 빨리 지울 수 없거나, 아예 지울 수 없다

즉 machine 자체 RAM 고갈이 아니라, command boundary에서의 policy failure인 경우가 많습니다.

read는 되는데 write만 실패할 수 있습니다

Redis 문서는 noeviction일 때 existing data를 읽는 command는 정상 동작한다고 분명히 설명합니다.

그래서 incident가 이런 모양으로 보이곤 합니다.

  • cache read는 계속 성공한다
  • health check도 살아 있다
  • memory-growing write만 OOM으로 실패한다

이 패턴 때문에 client bug처럼 보이기 쉽지만, 더 유용한 질문은 “pressure에서 Redis가 뭘 하도록 허용되어 있나?”입니다.

maxmemorymaxmemory-policy가 첫 번째 분기입니다

이 두 값만으로도 많은 사건이 설명됩니다.

설정 패턴보통 의미다음 확인 포인트
noevictionlimit 도달 시 memory-growing write 실패write path와 capacity 계획 확인
allkeys-*전체 dataset에서 eviction 가능eviction churn이 access pattern과 맞는지 확인
volatile-*TTL이 있는 key만 eviction 후보TTL coverage가 진짜인지 확인
volatile-ttl남은 TTL이 짧은 key 우선expiry 설계가 workload와 맞는지 확인

팀은 “오래된 cache key는 항상 사라질 수 있다”고 믿지만 policy가 그걸 실제로 허용하지 않는 경우가 많습니다.

volatile-*는 실제로 noeviction처럼 보일 수 있습니다

Redis 문서는 이 점을 아주 분명히 적어둡니다. volatile-* policy는 expiration이 달린 key가 하나도 없으면 noeviction처럼 동작합니다.

즉 cache가 이렇게 실패할 수 있습니다.

  • 팀은 volatile-* policy를 골랐다
  • 나중 write path가 persistent key를 많이 만들었다
  • Redis가 maxmemory에 닿았다
  • Redis는 eviction할 수 있는 key가 거의 없었다

이게 OOM incident가 유난히 의외처럼 느껴지는 대표적인 이유입니다.

실제 버그는 memory보다 missing TTL일 때가 많습니다

key가 expiration을 잃는 순간, volatile-* policy에서는 eviction candidate에서도 빠집니다. 그래서 이 incident는 Redis Keys Not Expiring와 직접 연결됩니다.

패턴은 보통 이렇습니다.

  • 앱은 이 데이터를 cache처럼 취급한다
  • 나중 write가 TTL을 지우거나 아예 TTL을 안 건다
  • key가 persistent가 된다
  • Redis는 합법적으로 eviction할 수 있는 key를 잃는다

큰 command는 순간적으로 limit를 더 넘길 수 있습니다

Redis eviction 문서는 많은 데이터를 추가하는 command가 eviction 전에 일시적으로 limit를 꽤 크게 초과할 수 있다고 설명합니다.

그래서 아래 같은 상황은 특히 봐야 합니다.

  • 큰 set 또는 sorted set write
  • batch cache population
  • 예상보다 큰 value 하나

즉 “마지막 command는 작아 보였는데요”만으로는 부족합니다. 직전 write shape를 같이 봐야 합니다.

replication과 persistence buffer까지 보면 그림이 넓어집니다

Redis 문서는 replication과 AOF buffer가 maxmemory eviction 계산에 포함되지 않는다고 설명하고, 이를 INFO memorymem_not_counted_for_evict로 노출합니다.

여기서 중요한 운영 분기가 생깁니다.

  • Redis는 key eviction 기준으로는 maxmemory를 잘 지키고 있다
  • host는 dataset보다 더 타이트하게 느껴질 수 있다
  • write pressure와 replica 또는 AOF buffer가 incident를 더 크게 보이게 만든다

mem_not_counted_for_evict가 의미 있게 크다면 dataset만 보고 capacity를 계획하면 안 됩니다.

used_memory_dataset로 data와 overhead를 분리할 수 있습니다

INFO memory는 dataset bytes를 used_memory_dataset으로, replica/AOF buffer처럼 eviction 계산 밖 메모리를 mem_not_counted_for_evict로 보여줍니다.

그래서 첫 분기가 쉬워집니다.

  • dataset 자체가 정말 너무 크다
  • policy 대비 key eligibility가 틀렸다
  • operational buffer가 node를 더 타이트하게 만든다

이게 Redis Memory Usage High를 다음으로 같이 봐야 하는 이유입니다.

--bigkeysMEMORY USAGE로 비싼 경로를 찾으세요

Redis MEMORY USAGE key [SAMPLES count]는 한 key의 RAM 비용을 보여주고, SAMPLES 0은 nested value를 더 넓게 훑습니다.

redis-cli --bigkeys와 같이 쓰면 강력합니다.

  • 구조적으로 큰 key를 찾고
  • 의심 key 하나의 실제 메모리 비용을 측정하고
  • 특정 기능 영역이 memory를 과도하게 먹는지 확인할 수 있습니다

한두 개 key가 incident를 지배한다면 다음 글은 보통 Redis Big Keys입니다.

흔한 원인

1. noeviction 하의 실제 pressure

Redis가 policy대로 정확히 동작하는 상황입니다.

2. volatile-*인데 TTL coverage가 약함

팀은 eviction을 기대했지만 실제 candidate pool이 너무 작습니다.

3. big key 또는 큰 batch write

특정 write path가 예상보다 훨씬 많은 메모리를 추가합니다.

4. cache-like key의 missing TTL

persistent key가 합법적인 eviction set를 잠식합니다.

5. replica 또는 AOF buffer가 pressure 그림을 키움

mem_not_counted_for_evict 때문에 dataset보다 node가 더 타이트하게 느껴집니다.

흔한 잘못된 시작

  • maxmemory도 안 보고 host RAM 고갈이라고 단정하기
  • eviction policy를 안 보고 application retry부터 바꾸기
  • volatile-*면 cache key가 다 TTL을 가지고 있다고 가정하기
  • big key나 TTL drift 확인도 안 하고 maxmemory부터 올리기
  • replica/persistence 환경에서 mem_not_counted_for_evict를 무시하기

실전 점검 순서

1. maxmemorymaxmemory-policy를 확인합니다

pressure에서 Redis가 무엇을 할 수 있는지부터 알아야 합니다.

2. eviction candidate가 실제로 존재하는지 확인합니다

volatile-* 환경에서는 이게 incident 전체일 때가 많습니다.

3. big key와 의심 write의 실제 비용을 확인합니다

막연한 memory 이야기를 측정 가능한 문제로 바꿔 줍니다.

4. mem_not_counted_for_evict를 확인합니다

dataset보다 node가 더 타이트하게 느껴지는 이유가 설명됩니다.

5. 답이 policy인지, TTL hygiene인지, capacity인지 결정합니다

이 분기 없이 한 가지 해법으로 뛰어들면 낭비가 큽니다.

체크리스트

  • maxmemory를 확인했다
  • maxmemory-policy를 확인했다
  • eviction candidate가 실제로 있는지 확인했다
  • big key 또는 memory-heavy key를 확인했다
  • mem_not_counted_for_evict를 확인했다

FAQ

Q. 이 OOM 메시지는 항상 서버 RAM이 바닥났다는 뜻인가요?

아니요. Redis 자신의 maxmemory 경계에 닿았고, 현재 policy로는 적절한 key를 비울 수 없다는 뜻인 경우가 많습니다.

Q. 왜 read는 되는데 write만 실패하죠?

noeviction에서는 read-only command는 계속 동작하고 memory-growing write만 거절될 수 있기 때문입니다.

Q. 왜 volatile-*인데도 OOM이 나죠?

expiration이 달린 key가 없으면 사실상 noeviction처럼 동작할 수 있기 때문입니다.

Q. 수상한 key 하나를 가장 빨리 보는 명령은 뭔가요?

MEMORY USAGE key SAMPLES 0TTL key입니다.

  • 소수 key가 dataset를 지배하면 Redis Big Keys가 맞습니다.

Sources:

먼저 읽어볼 가이드

검색 유입이 많은 핵심 글부터 이어서 보세요.

광고