# Eviction policies

> LRU, LFU, FIFO and TTLs: deciding what to drop when the cache is full.

Concept · Caching · https://levelcode.org/concepts/cache-eviction

A cache has a fixed amount of memory. When it's full and a new entry arrives, an **eviction policy** decides what to remove. The goal is to keep the items most likely to be read again.

| Policy | Evicts | Good for | Weak spot |
|---|---|---|---|
| LRU (least recently used) | The item not read for the longest | Most workloads: recency predicts reuse | A one-off scan of many keys flushes out the hot set |
| LFU (least frequently used) | The item read least often (with decay over time) | A stable set of consistently popular items | Slow to adapt when popularity shifts, without decay |
| FIFO | The oldest inserted item | Simplicity | Evicts hot items just because they're old |
| Random | Any item | Very cheap; surprisingly decent | Unpredictable |
| TTL / expiry | Only expired items | Bounding staleness | Doesn't free memory if nothing has expired |

Real caches combine TTL expiry with a capacity policy. Redis implements approximate LRU and LFU by sampling keys, which is far cheaper than tracking exact order. Newer designs combine a frequency sketch with recency and resist scans well: W-TinyLFU in Java's Caffeine, and TinyLFU admission in Go's Ristretto.

## Redis settings that matter

```ini
maxmemory 4gb
maxmemory-policy allkeys-lfu     # or allkeys-lru
```

- `allkeys-lru` / `allkeys-lfu`: evict any key — right for a pure cache.
- `volatile-lru` / `volatile-ttl`: evict only keys that have a TTL — for instances that mix cache data with data that must stay.
- `noeviction` (the default): reject writes when memory is full — right for a database, wrong for a cache.

## Watch

Track the hit rate and the eviction rate together. A high eviction rate with a falling hit rate means the cache is too small for the working set; more memory, or caching less per item, helps. See [caching patterns](https://levelcode.org/concepts/caching-patterns).
