Eviction is sampled, not exact

When maxmemory is exceeded Redis picks maxmemory-samples random keys and evicts the worst — so 'LRU' is approximate and tunable, not a doubly-linked list.

Previously

Persistence keeps data across crashes. But while the process is RUNNING and used_memory rises past maxmemory, someone has to go — and Redis doesn't actually keep a perfect LRU list.

Scene 05

Eviction is sampled, not exact

  1. Watch
  2. Try it
  3. Predict
  4. Capture
used / maxmemory6.00 MB / 10.0 MB · 60%POLICYallkeys-lruapprox LRU over all keyssamples = 5KEYSPACE · 50 keys1s7hot:02s8hot:13s9hot:23s8hot:31s6hot:42s6hot:52s6hot:61s9hot:71s7hot:82s8hot:937s0cold:036s0cold:149s2cold:259s2cold:318s2cold:420s2cold:55s1cold:621s2cold:717s1cold:846s0cold:911s2cold:1032s2cold:1115s1cold:1213s1cold:1347s2cold:1451s1cold:155s1cold:1664s2cold:1742s2cold:1810s1cold:1934s2cold:2032s2cold:2144s2cold:2241s0cold:2353s0cold:246s2cold:2529s0cold:2648s1cold:2756s0cold:2842s2cold:2964s1cold:3023s1cold:3160s0cold:3244s0cold:3343s1cold:345s2cold:3511s0cold:3641s0cold:3755s2cold:3864s2cold:39EVICTEDlast 0HIT RATE100%HOT SURVIVORS10 / 10approx LRU/LFU keeps hot keys alive when sample size is large enough
What to watch for

Hot keys (top row) get touched repeatedly while a stream of cold writes pushes used memory past maxmemory. When that happens Redis picks maxmemory-samples random keys, highlights them, and evicts the worst one — most hot keys survive, but not all.

Continue unlocks when the animation finishes.
Implementation

Highlighted lines are the ones running in the diagram right now.

freeMemoryIfNeeded
called before every write; evicts until under maxmemory
def freeMemoryIfNeeded():
if policy == 'noeviction':
return OK # caller checks separately
while used_memory() > maxmemory:
victim = pickVictim()
if victim is None:
return Error('cannot free memory')
evict(victim) # del + propagate to AOF/replicas
used_memory_subtract(sizeof(victim))
return OK
pickVictim (sampled LRU/LFU)
N random keys, evict the worst — not a global scan
def pickVictim():
pool = candidate_pool # carries near-misses across passes
samples = random_sample(keyspace, maxmemory_samples)
for k in samples:
pool.insert(k, score=lru_or_lfu(k))
# worst = highest idle time (LRU) or lowest counter (LFU)
return pool.pop_worst()
noeviction.handleWrite
the rejection path when memory is full
def handleWrite(cmd, value):
if used_memory() + sizeof(value) > maxmemory:
return Error(
'OOM command not allowed when used '
'memory > maxmemory'
)
# reads (GET, EXISTS, ...) keep serving normally
apply(cmd, value)
return OK

Where this sits in Build Redis

Scene 05 of 10, in the Memory act — Persistence, eviction, TTL — what makes RAM disappear.. maxmemory + sampled LRU/LFU — Redis only inspects N keys per pass; tunable via `maxmemory-samples`.

Up next. TTL & cleanup — eviction fires under memory pressure. The OTHER cleanup path is the clock: TTLs expire whether you're full or not.

All 10 scenes in Build Redis · Every curriculum

Built with Arqly
Every scene in Build Redis builds on the one before it.All 10 Build Redis scenes