fivenines

Theory / 10 min

Memory Limits And Eviction

Redis lives in memory, so memory pressure is not an edge case. It is part of the database model.

When maxmemory is configured, Redis needs an answer to a hard question: what should happen when a write would push the server beyond its memory budget?

At a high level:

incoming write -> check memory -> apply policy -> free space or reject -> execute command

That policy choice changes Redis from "an in-memory database" into several possible products: a strict store, an LRU-style cache, a TTL-constrained cache, or a frequency-aware cache.

Eviction Is Policy, Not Expiration

Expiration removes keys because their time has passed. Eviction removes keys because memory is scarce.

That difference matters because an evicted key did nothing wrong. It may be hot, valid, and nowhere near expiration. Redis deletes it because the configured policy says it is the best available sacrifice.

Common policy families include:

noeviction       reject writes when memory is full
allkeys-lru      evict approximated least recently used keys
volatile-lru     evict approximated LRU keys only among keys with TTL
allkeys-lfu      evict approximated least frequently used keys
volatile-lfu     LFU only among keys with TTL
allkeys-random   evict random keys
volatile-random  evict random keys only among keys with TTL
volatile-ttl     evict keys with the nearest expiration

The volatile-* policies are easy to misunderstand. They do not mean "evict volatile memory." They mean "only keys that already have an expiration are candidates."

Approximation Is The Point

Perfect LRU sounds attractive: always remove the key that has gone unused for the longest time. But exact LRU requires maintaining a perfectly ordered structure on every access. That overhead lands directly on the hot path.

Redis prefers approximation. It samples a small number of candidate keys, scores them according to the policy, evicts the best victim from the sample, and repeats if necessary.

sample candidates -> choose likely victim -> evict -> recheck memory

This makes eviction probabilistic but cheap. With a reasonable sample size, Redis gets behavior close enough to LRU or LFU without forcing every read and write to maintain a global ordering structure.

OOM Is A Command Dispatch Concern

Memory pressure affects which commands are safe to run. A read command should usually proceed. A write command that may allocate memory might need to trigger eviction or return an out-of-memory error.

This is where command metadata becomes useful. A command table can mark commands that should be denied when memory is exhausted and eviction cannot free enough space. The memory system should not need hand-written knowledge of every command scattered across the codebase.

Memory Accounting Is Imperfect

In a real server, memory is more complicated than key-value payloads. Client output buffers, replication backlog, allocator fragmentation, Lua or function memory, internal indexes, and copy-on-write during background persistence can all affect the process footprint.

So maxmemory is best understood as a policy boundary enforced through Redis' memory accounting, not a mathematical promise that the operating system will never see one byte more.

The practical question is whether Redis can keep itself within the intended envelope under normal workload and whether it fails predictably when it cannot.

Eviction Shapes The Product

With noeviction, Redis behaves like an in-memory database that refuses writes when full. With allkeys-lru, it behaves like a general cache. With volatile-ttl, it behaves like a cache where only keys designed to expire are eligible for removal.

The same engine supports these different personalities because eviction is explicit policy. That is the real lesson: memory pressure is not just something Redis survives. It is something Redis exposes as a configurable part of the system's meaning.

Next step

See what actually stuck.

Take the practice scenarios now.