Chapter 18 · Memory Management, Eviction, Expiration, Fragmentation, and OOM Prevention
maxmemory and Eviction Policies: noeviction, LRU, LFU, Random, and TTL-Aware Choices
Choose Redis maxmemory and eviction policies from AtlasMart workload semantics, prove noeviction and volatile-policy edge cases, and observe eviction rather than guessing from configuration.
Learning outcomes
AtlasMart mixes recomputable product-card cache entries with non-recomputable workflow state. A memory limit without a semantic eviction policy can either reject useful writes or silently remove data the application expected to retain.
Explain maxmemory as an eviction threshold rather than a process hard cap.
Distinguish noeviction, allkeys, volatile, LRU, LFU, random, and TTL-aware policies.
Prove the volatile-policy no-TTL edge case and observe actual eviction counters.
Use TTL and key-class semantics to decide whether eviction is permissible at all.
Keep memory-pressure labs bounded with a disposable container and measured results.
The course baseline remains Redis Open Source
8.10.1 from pinned image
redis:8.10.1. Memory-pressure experiments use
dedicated disposable Chapter 18 containers, named volumes, and
loopback-only host ports 6391–6395 so the shared Chapter 01
lab is never pushed toward eviction or out-of-memory behavior.
The temporary academy-admin password is
classroom-only. TLS is omitted only on loopback. Mandatory
topology is standalone, logical DB 0. Each lesson states its
own persistence, maxmemory, eviction, and container-memory
settings. Lesson 2 uses a 192 MiB container but deliberately
sets Redis maxmemory to 32 MiB. Persistence is disabled so
eviction behavior is isolated from AOF/RDB buffers.
1. Eviction is a business-state decision disguised as a memory setting
maxmemory tells Redis when to apply the configured
policy as commands add data. It does not tell Redis which
AtlasMart keys are safe to lose. That is an application
contract. Recomputable product-card cache keys may be
disposable; payment idempotency state may not be.
| Policy family | Eligible keys | Selection idea |
|---|---|---|
| noeviction | None | Reject memory-growing writes once the limit cannot be satisfied. |
| allkeys-lru / allkeys-lfu | Any key | Approximate recency/frequency among sampled candidates. |
| allkeys-random | Any key | Random candidate. |
| volatile-lru / volatile-lfu / volatile-random | Only keys with TTL | Approximate recency/frequency/random among volatile keys. |
| volatile-ttl | Only keys with TTL | Prefer keys with shortest remaining TTL. |
| allkeys-lrm / volatile-lrm (8.6+) | All or TTL keys | Approximate least-recently-modified; newer version-sensitive extension. |
2. Build the dedicated 32 MiB eviction node
docker volume create atlasmart-redis-ch18-evict_datadocker run --rm -v atlasmart-redis-ch18-evict_data:/data redis:8.10.1 sh -lc 'printf "%s\n" "user default off" "user academy-admin on >AtlasMart-Admin-Lab-Only-2026 ~* &* +@all" > /data/users.acl'docker run -d --name atlasmart-redis-ch18-evict --memory 192m -p 127.0.0.1:6392:6379 -v atlasmart-redis-ch18-evict_data:/data redis:8.10.1 redis-server --dir /data --aclfile /data/users.acl --save '' --appendonly no --maxmemory 32mb --maxmemory-policy noevictiondocker exec -e REDISCLI_AUTH=AtlasMart-Admin-Lab-Only-2026 atlasmart-redis-ch18-evict redis-cli --user academy-admin PINGdocker exec -e REDISCLI_AUTH=AtlasMart-Admin-Lab-Only-2026 atlasmart-redis-ch18-evict redis-cli --user academy-admin ACL WHOAMI
docker exec -e REDISCLI_AUTH=AtlasMart-Admin-Lab-Only-2026 atlasmart-redis-ch18-evict redis-cli --user academy-admin CONFIG GET maxmemory maxmemory-policy maxmemory-samplesdocker exec -e REDISCLI_AUTH=AtlasMart-Admin-Lab-Only-2026 atlasmart-redis-ch18-evict redis-cli --user academy-admin INFO memorydocker exec -e REDISCLI_AUTH=AtlasMart-Admin-Lab-Only-2026 atlasmart-redis-ch18-evict redis-cli --user academy-admin INFO stats
3. noeviction: writes can fail while reads continue
Do not predict the exact accepted-key count: key names, object
overhead, allocator state, and server baseline all consume
memory. The invariant is that once Redis cannot satisfy a
memory-growing write under noeviction, it rejects
that write rather than deleting an eligible key.
import os, redisHOST = "127.0.0.1"PORT = 6392r = redis.Redis(host=HOST, port=PORT, username="academy-admin", password="AtlasMart-Admin-Lab-Only-2026", decode_responses=False)assert r.ping()from redis.exceptions import ResponseErrorpayload = b"p" * 32768accepted = 0for i in range(2000): try: r.set(f"atlasmart:ch18:l2:noevict:{i}", payload) accepted += 1 except ResponseError as exc: print("first_rejection_after", accepted, type(exc).__name__, str(exc)[:120]) breakprint("existing_key_read", bool(r.get("atlasmart:ch18:l2:noevict:0")))print("stats", {k:r.info("stats").get(k) for k in ("evicted_keys","keyspace_hits","keyspace_misses","current_eviction_exceeded_time")})
Record the first rejection, existing-key reads, used_memory, and rejected command counters. A 32 MiB configured maxmemory is not 32 MiB of application payload.
4. allkeys-lru: make the same pressure evictable
Because this is a dedicated disposable node, changing its policy
is safe. Delete only the lesson prefix, switch to
allkeys-lru, warm one subset repeatedly, then add
cold keys until eviction occurs. Redis LRU is approximate, so
the lab verifies population-level behavior and counters rather
than asserting the exact next key that must disappear.
import os, redisHOST = "127.0.0.1"PORT = 6392r = redis.Redis(host=HOST, port=PORT, username="academy-admin", password="AtlasMart-Admin-Lab-Only-2026", decode_responses=False)assert r.ping()keys = list(r.scan_iter(match="atlasmart:ch18:l2:*", count=100))for start in range(0, len(keys), 100): if keys[start:start+100]: r.unlink(*keys[start:start+100])print("remaining_lesson_keys", sum(1 for _ in r.scan_iter(match="atlasmart:ch18:l2:*", count=100)))
docker exec -e REDISCLI_AUTH=AtlasMart-Admin-Lab-Only-2026 atlasmart-redis-ch18-evict redis-cli --user academy-admin CONFIG SET maxmemory-policy allkeys-lrudocker exec -e REDISCLI_AUTH=AtlasMart-Admin-Lab-Only-2026 atlasmart-redis-ch18-evict redis-cli --user academy-admin CONFIG GET maxmemory maxmemory-policy
import os, redisHOST = "127.0.0.1"PORT = 6392r = redis.Redis(host=HOST, port=PORT, username="academy-admin", password="AtlasMart-Admin-Lab-Only-2026", decode_responses=False)assert r.ping()payload = b"c" * 32768for i in range(200): r.set(f"atlasmart:ch18:l2:cache:{i}", payload)for _ in range(25): for i in range(20): r.get(f"atlasmart:ch18:l2:cache:{i}")before = r.info("stats")["evicted_keys"]for i in range(200, 1800): r.set(f"atlasmart:ch18:l2:cache:{i}", payload)after = r.info("stats")["evicted_keys"]hot_alive = sum(r.exists(f"atlasmart:ch18:l2:cache:{i}") for i in range(20))print("evictions_delta", after-before, "hot_alive", hot_alive, "of", 20)print("dbsize", r.dbsize())
5. volatile policies have a hard eligibility boundary
Under a volatile-* policy, only keys with an
expiration are eviction candidates. If there are no volatile
keys, Redis behaves like noeviction for
memory-growing writes. This is a correctness boundary, not just
a tuning detail.
docker exec -e REDISCLI_AUTH=AtlasMart-Admin-Lab-Only-2026 atlasmart-redis-ch18-evict redis-cli --user academy-admin CONFIG SET maxmemory-policy volatile-lrudocker exec -e REDISCLI_AUTH=AtlasMart-Admin-Lab-Only-2026 atlasmart-redis-ch18-evict redis-cli --user academy-admin SET atlasmart:ch18:l2:persistent-control keep-medocker exec -e REDISCLI_AUTH=AtlasMart-Admin-Lab-Only-2026 atlasmart-redis-ch18-evict redis-cli --user academy-admin SET atlasmart:ch18:l2:ttl-control evictable EX 600docker exec -e REDISCLI_AUTH=AtlasMart-Admin-Lab-Only-2026 atlasmart-redis-ch18-evict redis-cli --user academy-admin TTL atlasmart:ch18:l2:persistent-controldocker exec -e REDISCLI_AUTH=AtlasMart-Admin-Lab-Only-2026 atlasmart-redis-ch18-evict redis-cli --user academy-admin TTL atlasmart:ch18:l2:ttl-controldocker exec -e REDISCLI_AUTH=AtlasMart-Admin-Lab-Only-2026 atlasmart-redis-ch18-evict redis-cli --user academy-admin CONFIG GET maxmemory-policy
Choosing volatile-lru for a cache where most keys accidentally have no TTL can convert expected cache eviction into OOM write rejection. Treat “all cache keys have an appropriate TTL” as a tested application invariant.
6. TTL-aware does not mean “expire exactly on schedule”
volatile-ttl uses remaining TTL as the eviction
signal among TTL-bearing keys when memory pressure requires
eviction. That is separate from normal key expiration. Chapter 2
already established that expiration is not an exact scheduler;
memory eviction can remove a key before its TTL reaches zero.
| Question | Mechanism |
|---|---|
| Has the retention/cache lifetime ended? | Expiration/TTL semantics. |
| Must Redis reclaim memory now? | maxmemory eviction policy. |
| Which TTL-bearing key is a candidate? | volatile-* policy and its selection rule. |
7. Measure outcome: evictions, misses, and rejected commands
docker exec -e REDISCLI_AUTH=AtlasMart-Admin-Lab-Only-2026 atlasmart-redis-ch18-evict redis-cli --user academy-admin INFO statsdocker exec -e REDISCLI_AUTH=AtlasMart-Admin-Lab-Only-2026 atlasmart-redis-ch18-evict redis-cli --user academy-admin INFO memorydocker exec -e REDISCLI_AUTH=AtlasMart-Admin-Lab-Only-2026 atlasmart-redis-ch18-evict redis-cli --user academy-admin CONFIG GET maxmemory maxmemory-policydocker exec -e REDISCLI_AUTH=AtlasMart-Admin-Lab-Only-2026 atlasmart-redis-ch18-evict redis-cli --user academy-admin DBSIZE
Watch evicted_keys, expired_keys,
keyspace_hits/keyspace_misses, and
command rejection counters together. A policy that prevents OOM
but destroys hit rate or removes non-recomputable state is still
wrong.
8. Production policy matrix
| Workload | Likely starting question | Do not assume |
|---|---|---|
| Pure recomputable cache | Would allkeys-lru/LFU fit measured access skew? | That LRU is exact. |
| Mixed durable-like state + TTL cache | Can separate Redis instances make eviction eligibility explicit? | That volatile policy makes persistent keys safe from process OOM. |
| Strongly non-evictable state | Should noeviction fail writes and trigger scaling/admission control? | That maxmemory alone prevents host OOM. |
| Read-heavy stale-data eviction (8.6+) | Would LRM better represent value than access recency? | That every managed service exposes LRM immediately. |
9. Verification checklist
- Observe at least one noeviction rejection without exhausting host/container memory.
-
Observe an increase in
evicted_keysunder an allkeys policy. - Prove which fixture keys have TTL before using a volatile policy.
- Never treat a volatile policy as a substitute for application retention contracts.
- Record Redis version and policy because Redis 8.6+ adds LRM options absent from older servers.
docker rm -f atlasmart-redis-ch18-evictdocker volume rm atlasmart-redis-ch18-evict_data# Removes only this named Chapter 18 node and volume.
Check your understanding
- What happens to volatile-lru when no keys have TTLs?
- Why is maxmemory-policy a data-model decision?
- Does volatile-ttl wait for a key to expire before evicting it?
- Why can reads still work under noeviction after writes are rejected?
Review the answers
It behaves like noeviction for memory-growing writes because there are no eligible eviction candidates.
Because the policy defines which application keys Redis may delete under pressure; that must match whether data is recomputable or authoritative.
No. Under memory pressure it can evict a TTL-bearing key before its TTL deadline, preferring shorter remaining TTLs.
noeviction rejects commands that need to allocate more cache data; reads of existing state continue normally.
Production judgment and next bridge
Choose eviction only after classifying data loss semantics and measuring pressure behavior. Lesson 3 now examines why Redis LRU and LFU are deliberately approximate and how sampling, decay, and workload skew affect hit rate.
Summary and next step
maxmemory and Eviction Policies: noeviction, LRU, LFU, Random, and TTL-Aware Choices is now connected to observable Redis behavior, bounded failure cases, and production tradeoffs. Keep the evidence and cleanup state from this lesson; next, continue with Approximate LRU/LFU Sampling, LFU Decay, Hot-Set Behavior, and Cache Hit Rate.
Authoritative references
- Redis key eviction
- Redis memory optimization
- INFO
- MEMORY STATS
- MEMORY USAGE
- MEMORY DOCTOR
- MEMORY PURGE
- MEMORY MALLOC-STATS
- OBJECT FREQ
- OBJECT IDLETIME
- UNLINK
- Redis CLI keyspace analysis
- Redis latency monitoring
- Redis persistence
- Redis replication
- Redis Sentinel
- Redis Cluster specification
- Redis Search and query
- Redis 8.10 release notes
- Redis 8.10 whats new
- Redis ACLs
- Redis security
- Redis licenses
- Docker Official Redis image