The MRU (Most Recently Used) Cache Pattern: When Recent Access Means You're Done With It

2026-07-13

Everyone reaches for LRU by default. But there's a class of workloads where LRU is exactly backwards — and MRU (Most Recently Used) eviction, which throws out the item you just touched, is the right answer.

The core insight: LRU assumes recency predicts future access. MRU assumes the opposite: if you just accessed something, you're done with it and won't come back soon. This sounds bizarre until you see the access pattern.

The canonical case: sequential scans over a working set larger than cache. Imagine a 10GB table, a 2GB cache, and a nightly job that scans the whole table row by row. With LRU:

LRU pessimizes this workload. Every eviction throws out the block you're most likely to need next cycle, and keeps the block you just finished with.

MRU flips this. When the cache is full and you need to load row 2GB+1, you evict the row you just read — because you're scanning forward and won't touch it again this pass. The rows from the start of the scan stay cached, so the next full scan gets partial hits on the front of the table.

Real-world use: PostgreSQL's buffer manager uses a variant of this idea. When it detects a sequential scan larger than 25% of shared_buffers, it switches to a small ring buffer (256KB by default) instead of polluting the main LRU cache. The scan reuses its own tiny window, evicting its own recent reads, and leaves the hot OLTP working set untouched. Same principle: recent access under sequential scan means "done with it."

Rule of thumb: If your access pattern is looping or sequential over a set larger than cache, LRU gives you 0% hit rate and MRU gives you (cache_size / working_set) × 100%. For a 2GB cache over 10GB of data, that's 20% instead of 0%. For anything else — Zipfian, temporal locality, hot keys — stick with LRU or LFU.

The trap: Don't apply MRU globally. It's a targeted tool for a specific access pattern. Most caches serve mixed workloads, and MRU on random access is catastrophically wrong. The real lesson is: detect the scan, isolate it, and apply the right policy to that stream — don't let one bad workload evict everyone else's hot data.

See it in action: Check out LRU Cache - Twitch Interview Question - Leetcode 146 by NeetCode to see this theory applied.
Key Takeaway: LRU assumes recent = valuable; MRU assumes recent = finished — use MRU (or a scan-resistant ring buffer) when workloads loop or scan sequentially through more data than fits in cache.

All newsletters