Chapter 18 · Memory Management, Eviction, Expiration, Fragmentation, and OOM Prevention
used_memory vs RSS, Allocator Overhead, Fragmentation, Buffers, and Replication/AOF Memory
Separate Redis logical allocation from process RSS and allocator fragmentation, then account for client, AOF, replication, and other memory that can sit outside the eviction budget.
Learning outcomes
AtlasMart sees a container at 70% of its memory limit while Redis reports a much smaller dataset. The discrepancy is not automatically a leak: Redis exposes several layers of memory that answer different questions. This lesson builds the vocabulary before any eviction policy is changed.
Distinguish used_memory, used_memory_dataset, allocator metrics, and process RSS.
Interpret fragmentation using allocator and RSS metrics rather than one ratio in isolation.
Identify client, AOF, replication, and other buffers that consume process memory.
Explain why maxmemory is an eviction/data budget rather than a hard RSS ceiling.
Measure allocation/freeing behavior safely on a dedicated node and preserve headroom for background work.
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 1 enables AOF everysec but leaves maxmemory
at 0 so memory layers can be observed without eviction. The
container is capped at 192 MiB only as a host-safety boundary,
not as a recommended production ratio.
1. Four numbers, four different questions
used_memory is Redis allocator-accounted memory.
used_memory_dataset estimates the dataset portion
after Redis overhead. allocator_active includes
active allocator pages and therefore external fragmentation.
used_memory_rss is resident memory reported by the
operating system for the Redis process. RSS can stay elevated
after keys are deleted because the allocator may retain pages
for reuse instead of immediately returning them to the OS.
| Metric | Question it answers | Common mistake |
|---|---|---|
| used_memory | How many bytes Redis currently accounts through its allocator? | Calling this the whole process footprint. |
| used_memory_dataset | How much allocator-accounted memory belongs to data rather than Redis overhead? | Treating it as serialized value bytes only. |
| allocator_active / allocator_allocated | How much external allocator fragmentation exists? | Using mem_fragmentation_ratio alone. |
| used_memory_rss | How many resident pages does the process occupy now? | Expecting it to fall immediately after DEL/UNLINK. |
mem_fragmentation_ratio = used_memory_rss /
used_memory
includes allocator fragmentation plus other RSS overhead such
as code, libraries, and stacks. Redis documents
allocator_frag_ratio = allocator_active /
allocator_allocated
as the more direct external-fragmentation signal.
2. Start a bounded node and take a baseline
docker volume create atlasmart-redis-ch18-memory_datadocker run --rm -v atlasmart-redis-ch18-memory_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-memory --memory 192m -p 127.0.0.1:6391:6379 -v atlasmart-redis-ch18-memory_data:/data redis:8.10.1 redis-server --dir /data --aclfile /data/users.acl --appendonly yes --appendfsync everysec --save '' --maxmemory 0 --maxmemory-policy noevictiondocker exec -e REDISCLI_AUTH=AtlasMart-Admin-Lab-Only-2026 atlasmart-redis-ch18-memory redis-cli --user academy-admin PINGdocker exec -e REDISCLI_AUTH=AtlasMart-Admin-Lab-Only-2026 atlasmart-redis-ch18-memory redis-cli --user academy-admin ACL WHOAMI
docker exec -e REDISCLI_AUTH=AtlasMart-Admin-Lab-Only-2026 atlasmart-redis-ch18-memory redis-cli --user academy-admin INFO memorydocker exec -e REDISCLI_AUTH=AtlasMart-Admin-Lab-Only-2026 atlasmart-redis-ch18-memory redis-cli --user academy-admin MEMORY STATSdocker exec -e REDISCLI_AUTH=AtlasMart-Admin-Lab-Only-2026 atlasmart-redis-ch18-memory redis-cli --user academy-admin MEMORY DOCTORdocker exec -e REDISCLI_AUTH=AtlasMart-Admin-Lab-Only-2026 atlasmart-redis-ch18-memory redis-cli --user academy-admin CONFIG GET maxmemory maxmemory-policy appendonly appendfsync
Record the actual allocator name. MEMORY PURGE and
allocator internals are jemalloc-specific; on another allocator,
a purge can be a benign no-op.
3. Grow logical data and observe allocator/process growth
The payload is deterministic and bounded: 1,200 keys × 8 KiB is under 10 MiB of raw payload before key/object/allocator overhead. Do not infer an exact overhead percentage from one run; the encoding, allocator, architecture, and Redis version affect it.
import os, redisHOST = "127.0.0.1"PORT = 6391r = redis.Redis(host=HOST, port=PORT, username="academy-admin", password="AtlasMart-Admin-Lab-Only-2026", decode_responses=False)assert r.ping()payload = b"x" * 8192pipe = r.pipeline(transaction=False)for i in range(1200): pipe.set(f"atlasmart:ch18:l1:blob:{i}", payload)pipe.execute()m = r.info("memory")for k in ("used_memory","used_memory_dataset","used_memory_rss","allocator_allocated","allocator_active","allocator_resident","mem_fragmentation_bytes","mem_not_counted_for_evict","mem_aof_buffer"): print(k, m.get(k))
docker exec -e REDISCLI_AUTH=AtlasMart-Admin-Lab-Only-2026 atlasmart-redis-ch18-memory redis-cli --user academy-admin DBSIZEdocker exec -e REDISCLI_AUTH=AtlasMart-Admin-Lab-Only-2026 atlasmart-redis-ch18-memory redis-cli --user academy-admin MEMORY USAGE atlasmart:ch18:l1:blob:0docker exec -e REDISCLI_AUTH=AtlasMart-Admin-Lab-Only-2026 atlasmart-redis-ch18-memory redis-cli --user academy-admin INFO memorydocker exec -e REDISCLI_AUTH=AtlasMart-Admin-Lab-Only-2026 atlasmart-redis-ch18-memory redis-cli --user academy-admin MEMORY STATS
4. Freeing logical data does not promise an immediate RSS drop
UNLINK removes a key from the keyspace immediately
and lets background threads reclaim the value later. Even after
Redis frees those allocations, jemalloc may keep pages resident
for reuse. This is why “DBSIZE fell but RSS did not” is not
sufficient evidence of a leak.
import os, redisHOST = "127.0.0.1"PORT = 6391r = redis.Redis(host=HOST, port=PORT, username="academy-admin", password="AtlasMart-Admin-Lab-Only-2026", decode_responses=False)assert r.ping()keys = [f"atlasmart:ch18:l1:blob:{i}" for i in range(0, 1200, 2)]for start in range(0, len(keys), 100): r.unlink(*keys[start:start+100])print("remaining", r.dbsize())print("memory", {k:r.info("memory").get(k) for k in ("used_memory","used_memory_rss","lazyfree_pending_objects","lazyfreed_objects")})
docker exec -e REDISCLI_AUTH=AtlasMart-Admin-Lab-Only-2026 atlasmart-redis-ch18-memory redis-cli --user academy-admin INFO memorydocker exec -e REDISCLI_AUTH=AtlasMart-Admin-Lab-Only-2026 atlasmart-redis-ch18-memory redis-cli --user academy-admin MEMORY DOCTORdocker exec -e REDISCLI_AUTH=AtlasMart-Admin-Lab-Only-2026 atlasmart-redis-ch18-memory redis-cli --user academy-admin MEMORY PURGEdocker exec -e REDISCLI_AUTH=AtlasMart-Admin-Lab-Only-2026 atlasmart-redis-ch18-memory redis-cli --user academy-admin INFO memory# MEMORY PURGE asks the allocator to return reclaimable pages; it is not a guarantee that RSS reaches used_memory.
5. maxmemory intentionally excludes some transient buffers
Redis documents mem_not_counted_for_evict as
memory, principally transient replica and AOF buffers, that is
not counted when deciding whether key eviction is required. The
reason is to avoid an eviction feedback loop: evicting a key
itself generates replication/AOF traffic. The consequence is
operationally important—maxmemory cannot equal the container or host RAM
limit.
| Memory component | Compared to maxmemory? | Budget implication |
|---|---|---|
| Dataset and ordinary Redis allocations | Generally yes | This is the core eviction budget. |
| Transient replica/AOF buffers | Excluded via mem_not_counted_for_evict | Leave separate headroom. |
| Allocator/RSS slack | Not a hard maxmemory-controlled ceiling | Size the process/container for peak RSS, not just dataset. |
| Fork copy-on-write | Temporary physical-memory pressure | Reserve background persistence/full-sync headroom. |
| Search/vector/time-series indexes | Consume Redis/process memory | Measure their actual footprint and include it in the data/platform budget. |
docker exec -e REDISCLI_AUTH=AtlasMart-Admin-Lab-Only-2026 atlasmart-redis-ch18-memory redis-cli --user academy-admin INFO memorydocker exec -e REDISCLI_AUTH=AtlasMart-Admin-Lab-Only-2026 atlasmart-redis-ch18-memory redis-cli --user academy-admin INFO persistencedocker exec -e REDISCLI_AUTH=AtlasMart-Admin-Lab-Only-2026 atlasmart-redis-ch18-memory redis-cli --user academy-admin INFO replication
6. Redis 8.10 adds another memory dimension: compact-hash templates
Redis 8.10 adds compact hashes and reports
used_memory_hash_templates in
INFO MEMORY. MEMORY USAGE for a
compact-hash key includes that key's proportional share of a
shared template. This is a useful reminder that per-key memory
accounting can include shared structures and that a memory model
frozen on an older release can become incomplete.
Do not assume used_memory_hash_templates exists
on every Redis 8.x release. It is an 8.10 metric.
7. Deliberately wrong model: “set maxmemory to container RAM”
Suppose AtlasMart allocates a 2 GiB container and sets
maxmemory 2gb. That leaves no room for excluded
buffers, RSS/allocator slack, persistence or replication COW,
client buffers, executable/library pages, or other process
overhead. The kernel/container runtime—not Redis eviction—may
become the final limiter.
Start from the process/container envelope and subtract measured non-evictable and transient headroom before choosing maxmemory. Re-measure at peak write rate, during BGSAVE/AOF rewrite/full sync, and after failover topology changes.
8. Verification checklist
-
Capture
used_memory,used_memory_dataset,used_memory_rss, allocator metrics, andmem_not_counted_for_evicttogether. - Verify RSS may remain above logical allocation after deletes without claiming a leak.
-
Confirm AOF/replication buffers are budgeted separately from
maxmemory. - Record the allocator before relying on jemalloc-specific diagnostics.
- Never infer a production headroom percentage from this tiny fixture.
docker rm -f atlasmart-redis-ch18-memorydocker volume rm atlasmart-redis-ch18-memory_data# Removes only this named Chapter 18 node and volume.
Check your understanding
- Why can RSS stay high after many keys are removed?
- Why is mem_not_counted_for_evict operationally important?
- Is mem_fragmentation_ratio a pure allocator-fragmentation metric?
- Does maxmemory guarantee the Redis process cannot exceed that many bytes?
Review the answers
Because allocator pages can remain resident and reusable even after Redis frees logical allocations; process RSS is not identical to current dataset bytes.
It represents memory such as transient AOF/replication buffers that is not compared with maxmemory, so the process can exceed the eviction budget.
No. It compares process RSS with used_memory and includes non-allocator RSS overhead; allocator_frag_ratio is the more direct external-fragmentation signal.
No. It is an eviction/data limit with excluded buffers and other process/RSS overhead outside it.
Production judgment and next bridge
Monitor memory as a vector of signals, not one percentage. Alert
on available host/container memory, RSS, allocator fragmentation
bytes, excluded buffers, client buffers, fork/COW peaks, and the
workload-specific index footprint. Lesson 2 now turns that
measured envelope into an explicit maxmemory and
eviction-policy choice.
Summary and next step
used_memory vs RSS, Allocator Overhead, Fragmentation, Buffers, and Replication/AOF Memory 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 maxmemory and Eviction Policies: noeviction, LRU, LFU, Random, and TTL-Aware Choices.
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