Redis OOM command not allowed: Set maxmemory-policy
Redis OOM command not allowed: Set maxmemory-policy
If Redis is only a cache, run redis-cli CONFIG SET maxmemory-policy allkeys-lru and put the same setting in redis.conf (or your managed service's parameter group): writes go through again, because Redis now evicts least recently used keys instead of refusing new ones. If the instance also holds queues, sessions or locks, keep volatile-lru and write every cache key with a TTL (SET key value EX 3600), because the volatile policies can only evict keys that have one.
This story was written with the assistance of an AI writing program. I reproduced the error on Redis 8.10.2 with redis-py 8.1.0 and Python 3.14.4, with maxmemory at 20 MB and 1 KiB values so the instance fills in seconds. Every number below comes from those runs.
What the Error Blocks, and What It Doesn't
Under the default policy, noeviction, the 15,201st SET failed. redis-py raised redis.exceptions.OutOfMemoryError: command not allowed when used memory > 'maxmemory'.; redis-cli prints the same text after OOM. evicted_keys was 0.
Not every command failed. GET, EXISTS, EXPIRE and DEL still worked; SET, INCR and LPUSH were refused. Redis rejects commands that can grow memory and lets reads and deletes through, so the outage looks partial: cache reads keep hitting while writes, counters and job enqueues fail. Deleting 1,000 keys let the next SET in.
Batches split too. A MULTI/EXEC with EXPIRE, SET and INCR was rejected whole, EXPIRE included. The same three commands in a plain pipeline applied the EXPIRE and failed the other two.
volatile-lru Without TTLs Behaves Like noeviction
Self-hosted Redis defaults to noeviction; managed services often default to volatile-lru. I switched to volatile-lru and refilled with keys that had no TTL. The 15,200th SET failed with the same error and nothing was evicted: the volatile policies only choose among keys with an expiry, and there were none. Four commands show it:
$ redis-cli CONFIG GET maxmemory-policy maxmemory-policy volatile-lru $ redis-cli INFO keyspace # Keyspace db0:keys=15199,expires=0,avg_ttl=0,subexpiry=0 $ redis-cli INFO stats | grep evicted_keys evicted_keys:0 $ redis-cli SET probe 1 OOM command not allowed when used memory > 'maxmemory'. expires=0 next to thousands of keys means the policy has nothing to evict. With ex=3600 on every write, the same instance accepted 60,000 of 60,000 writes, evicted 45,155 keys and kept 14,845.
allkeys-lru Fixes a Cache and Empties a Queue
Under allkeys-lru with no TTLs, all 60,000 writes succeeded and 44,863 keys were evicted. None of the first 1,000 keys written were left; 982 of the last 1,000 were. For a pure cache, that is the behavior you want.
Then I put a 3,000-item list named jobs on the instance before the cache writes, the way small apps share one Redis between a task queue and a cache. Under allkeys-lru every cache write succeeded and the list was gone: EXISTS jobs returned 0, and nothing raised an error. Under volatile-lru with a TTL on each cache key, all 60,000 cache writes succeeded and the list kept its 3,000 items. Under noeviction the list survived, but cache writes failed after 12,489.
allkeys-lru belongs on an instance that holds nothing you cannot recompute. Anything else gets volatile-lru plus TTLs, or better, its own instance.
Put a TTL on Every Cache Write
This is the write path I tested:
import redis r = redis.Redis(host="localhost", port=6379) def cache_set(key, value, ttl=3600): try: r.set(key, value, ex=ttl) # a TTL makes the key evictable under volatile-lru except redis.exceptions.OutOfMemoryError: return False # full and nothing evictable: skip the cache, keep serving return True I ran it unchanged against a local Redis on port 6379. On a fresh volatile-lru instance it returned True 60,000 times out of 60,000 while Redis evicted 45,135 keys. On a full instance whose 15,199 existing keys had no TTL, it returned False 2,000 times out of 2,000: requests were still served, but nothing new was cached. The except keeps the app up, so count those False returns and alert on them.
Making a Full Instance Writable Again
On a full noeviction instance, CONFIG SET maxmemory-policy allkeys-lru took effect at once: the next 2,000 cache writes succeeded, with 2,051 evictions. A CONFIG SET is lost on restart, so persist the change in the config too.
If the instance has to stay on volatile-lru, you can add TTLs while it is full, because EXPIRE is not refused. I set a 600-second TTL on 3,000 keys that had none; all 3,000 calls succeeded, and the next 2,000 writes went through with 2,092 evictions. In production that is a SCAN over the cache key pattern with an EXPIRE per match.
One Big Write Can Fail Below the Limit
With 12,000 keys, used_memory was 16,744,312 bytes, about 16 of the 20 MB. One 8 MB SET was still refused under noeviction, and used_memory was below the limit before and after. My reading is that the 8 MB argument is already in Redis's memory when it checks the limit, so the request counts against itself. Under allkeys-lru the same write succeeded after 4,794 evictions, and used_memory read 22,416,912 bytes right after, above the 20,971,520 limit. maxmemory is a target Redis evicts toward, not a hard cap, so leave headroom below the container or VM limit.
Takeaways
- Check
CONFIG GET maxmemory-policyandINFO keyspacefirst.noeviction, or avolatile-*policy withexpires=0, means Redis has nothing it may evict. - Pure cache:
allkeys-lru, persisted in config, not only set at runtime. - Cache plus queues or sessions:
volatile-lruand a TTL on every cache write, or separate instances. - If none of the data is evictable, the fix is a bigger
maxmemoryor less data, not a policy. - Catch
OutOfMemoryErroron cache writes and alert on it; reads keep working, so the outage looks partial. - Use
MULTIwhen writes must land together; a plain pipeline can apply half of them.
原文:https://www.a8q9.cn/en/article/redis-oom-command-not-allowed-set-maxmemory-policy-498923.html