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.

Advanced180–250 minutesmaxmemory, noeviction, LRU/LFU/random/TTL-aware evictionRedis Open Source 8.10.1Docker + redis-py 8.1.0Free/local-firstLast reviewed: September 6, 2026

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.

01

Explain maxmemory as an eviction threshold rather than a process hard cap.

02

Distinguish noeviction, allkeys, volatile, LRU, LFU, random, and TTL-aware policies.

03

Prove the volatile-policy no-TTL edge case and observe actual eviction counters.

04

Use TTL and key-class semantics to decide whether eviction is permissible at all.

05

Keep memory-pressure labs bounded with a disposable container and measured results.

Exact Chapter 18 lab boundary

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

Shell · create one isolated memory 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
redis-cli · prove the active budget
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.

Python · bounded noeviction experiment
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")})
Evidence, not folklore

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.

Python · incrementally reset only the lesson prefix
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)))
redis-cli · choose LRU after bounded cleanup
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
Python · create warm and cold populations
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.

redis-cli · verify TTL eligibility
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
Wrong approach

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

redis-cli · evidence after pressure
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_keys under 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.
Shell · bounded cleanup
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

  1. What happens to volatile-lru when no keys have TTLs?
  2. Why is maxmemory-policy a data-model decision?
  3. Does volatile-ttl wait for a key to expire before evicting it?
  4. 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

Keep knowledge open

Help the academy stay free and grow.

If these tutorials save you time, a small donation supports new lessons, technical review, diagrams, examples, and long-term maintenance.

ETHEthereum / ERC-20 only
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0

Send only Ethereum or ERC-20 compatible assets to this address.