Build Redis (10 scenes)
Scene 05 · Eviction is sampled, not exact
maxmemory + sampled LRU/LFU — Redis only inspects N keys per pass; tunable via `maxmemory-samples`.
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
Diagram
A grid of keys, each cell showing its size, idle-time, and access-count badges. On the right, the maxmemory bar fills as new writes land. When it crosses the line, the sampler highlights N random cells (you control N), scores them by the active policy (LRU / LFU), and the worst one fades out — eviction is approximate by design, never a perfect global pick.
used / maxmemory6.00 MB / 10.0 MB · 60%POLICYallkeys-lruapprox LRU over all keyssamples = 5KEYSPACE · 50 keys2s9hot:01s7hot:12s9hot:23s9hot:33s8hot:41s9hot:53s9hot:61s8hot:73s8hot:81s9hot:942s1cold:037s0cold:148s0cold:211s0cold:325s2cold:431s1cold:528s2cold:659s0cold:742s0cold:811s2cold:916s2cold:1055s1cold:1124s2cold:1234s0cold:1359s0cold:1437s0cold:157s2cold:1652s2cold:1718s1cold:1847s1cold:1921s0cold:2050s1cold:2138s0cold:2261s0cold:2349s1cold:2455s1cold:2538s1cold:2611s2cold:2748s0cold:2863s2cold:2918s0cold:3012s2cold:3116s0cold:3264s0cold:337s0cold:3457s2cold:3534s0cold:3624s2cold:3753s0cold:387s1cold:39EVICTEDlast 0HIT RATE100%HOT SURVIVORS10 / 10approx LRU/LFU keeps hot keys alive when sample size is large enough
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.
Implementation
freeMemoryIfNeeded
called before every write; evicts until under maxmemory
1def freeMemoryIfNeeded():
2 if policy == 'noeviction':
3 return OK # caller checks separately
4 while used_memory() > maxmemory:
5 victim = pickVictim()
6 if victim is None:
7 return Error('cannot free memory')
8 evict(victim) # del + propagate to AOF/replicas
9 used_memory_subtract(sizeof(victim))
10 return OK
pickVictim (sampled LRU/LFU)
N random keys, evict the worst — not a global scan
1def pickVictim():
2 pool = candidate_pool # carries near-misses across passes
3 samples = random_sample(keyspace, maxmemory_samples)
4 for k in samples:
5 pool.insert(k, score=lru_or_lfu(k))
6 # worst = highest idle time (LRU) or lowest counter (LFU)
7 return pool.pop_worst()
noeviction.handleWrite
the rejection path when memory is full
1def handleWrite(cmd, value):
2 if used_memory() + sizeof(value) > maxmemory:
3 return Error(
4 'OOM command not allowed when used '
5 'memory > maxmemory'
6 )
7 # reads (GET, EXISTS, ...) keep serving normally
8 apply(cmd, value)
9 return OK