Skip to main content
Back to the DSA atlas
Systems & storage Technique Advanced

Cache Eviction Strategies (LRU, LFU, ARC, TinyLFU)

Treat cache eviction as an algorithm choice: recency, frequency, adaptation, and admission all optimize different workloads.

cachelrulfutinylfu

Interactive visualization

Use the controls to connect the idea to concrete operations before diving into the full write-up.

What the policy optimizes

keep most recently touched items

Trade-off

Recency-only. Simple and common, but repeated one-hit wonders can still pollute the cache.

Family

Systems & storage

Database indexes, cache policies, query operators, sharding, and external-memory algorithms.

Builds on

2 topics

Read these first if you want the surrounding context.

Unlocks

0 next topics

Use these follow-ups when you want to apply the current idea in a richer setting.

Learning paths

1

This topic appears in curated progressions so you can keep moving without guessing the next step.

Choose this over that

Match the cache policy to the workload

The right policy depends on whether recency, long-term popularity, adaptation, or admission quality is the dominant signal.

Policy variant

LRU

Choose this when: Recent accesses are the best predictor and you want the simplest `O(1)` implementation.

Choose something else when: Short scans or one-hit traffic keep flushing genuinely hot items.

Policy variant

LFU

Choose this when: Stable hot keys should survive even if they are quiet for a short period.

Choose something else when: Old popularity quickly becomes stale.

Policy variant

ARC

Choose this when: The workload swings between recency-heavy and frequency-heavy behavior and the cache should adapt.

Choose something else when: The implementation must stay as simple as possible.

Policy variant

TinyLFU

Choose this when: Admission quality matters, especially when many one-hit arrivals compete with a smaller hot set.

Choose something else when: Approximate counting and segmented cache policy feel like too much machinery for the workload.

Problem

A cache with fixed capacity eventually has to answer the only question that really matters:

when the next item arrives, which old item should leave?

That choice is not bookkeeping. It is the algorithm that determines hit rate, tail latency, and whether the cache learns the right reuse pattern from the workload.

Intuition

A cache is trying to predict the future from the past.

Different policies use different signals:

  • recency: if an item was used just now, it may be used again soon
  • frequency: if an item has been used many times, it may be worth keeping even after a quiet period
  • adaptation: workloads change, so the cache may need to rebalance how much it trusts recency vs. frequency
  • admission: sometimes the best decision is to reject a new item instead of letting it evict something valuable

That is why LRU, LFU, ARC, and TinyLFU are not small variations of one trick. They encode different hypotheses about reuse.

LRU: least recently used

LRU is the canonical interview and systems baseline because it has a clean O(1) implementation.

Data structure

Use two structures together:

  1. a hash map from key to node for O(1) lookup
  2. a doubly linked list ordered from most-recently-used to least-recently-used

The head is the freshest item. The tail is the eviction candidate.

Core operations

get(key):
    if key not in map:
        return MISS
    node <- map[key]
    remove node from its current list position
    insert node at list head
    return node.value

put(key, value):
    if key in map:
        node <- map[key]
        node.value <- value
        remove node from its current list position
        insert node at list head
        return

    if cache is full:
        victim <- list tail
        remove victim from list
        delete map[victim.key]

    node <- new node(key, value)
    insert node at list head
    map[key] <- node

Worked example

Suppose capacity is 3 and the access stream is:

A, B, C, A, D

The recency order evolves like this:

  • after A, B, C: [C, B, A]
  • access A: move A to the front -> [A, C, B]
  • insert D: evict tail B -> [D, A, C]

LRU does exactly what its name says: it assumes the oldest untouched entry is the safest victim.

LFU: least frequently used

LFU keeps the items that have accumulated the strongest reuse history.

Data structure

A practical O(1) LFU implementation uses:

  • a hash map key -> node
  • a hash map frequency -> doubly linked list of nodes with that frequency
  • a variable minFreq storing the smallest frequency currently present

Each node stores (key, value, freq).

Core operations

touch(node):
    oldFreq <- node.freq
    remove node from freqList[oldFreq]
    if freqList[oldFreq] became empty and oldFreq == minFreq:
        minFreq <- minFreq + 1

    node.freq <- oldFreq + 1
    insert node at head of freqList[node.freq]

get(key):
    if key not in map:
        return MISS
    node <- map[key]
    touch(node)
    return node.value

put(key, value):
    if capacity == 0:
        return

    if key in map:
        node <- map[key]
        node.value <- value
        touch(node)
        return

    if cache is full:
        victim <- tail of freqList[minFreq]
        remove victim
        delete map[victim.key]

    node <- new node(key, value, freq = 1)
    insert node at head of freqList[1]
    map[key] <- node
    minFreq <- 1

When multiple items have the same frequency, most implementations break ties by recency inside each frequency bucket.

Where LFU wins and loses

LFU can beat LRU on workloads with stable hot keys, but it can also cling to stale popularity. Something that was hot an hour ago may keep surviving even if the workload has moved on.

That is why real systems often add aging, decay, or admission logic instead of relying on pure lifetime counts forever.

ARC and TinyLFU: advanced follow-ups

ARC

Adaptive Replacement Cache tries to avoid hard-coding how much to trust recency vs. frequency.

It keeps four lists:

  • T1: recent entries seen once
  • T2: frequent entries seen at least twice
  • B1: ghost history of items recently evicted from T1
  • B2: ghost history of items recently evicted from T2

If misses keep hitting B1, ARC increases the space devoted to recency. If misses keep hitting B2, ARC shifts capacity toward frequency.

The key idea is that the ghost lists store only metadata, not values, so ARC can learn from evictions without paying full cache cost.

TinyLFU

TinyLFU separates admission from eviction.

Instead of blindly inserting every missed item, it asks whether the new candidate is likely to be more valuable than the current victim.

A common design is Window TinyLFU:

  1. keep a small LRU window for new arrivals
  2. keep a main segmented cache for long-lived winners
  3. maintain approximate frequency counts with a Count-Min Sketch
  4. when a candidate competes with a victim, admit the candidate only if its estimated frequency is higher
admit(candidate, victim):
    if estimate(candidate.key) >= estimate(victim.key):
        keep candidate
    else:
        reject candidate and keep victim

This is why TinyLFU is so strong in real caches: one-hit wonders do not get to evict items that have already proved their value.

Complexity and when to choose each policy

PolicyCore structureLookup / updateBest whenMain weakness
LRUHash map + doubly linked listO(1)Reuse is strongly tied to recent accessesScans can flush genuinely valuable items
LFUHash map + frequency bucketsO(1) with bucket listsHot keys stay hot for long periodsOld popularity can become stale
ARCMultiple recency/frequency lists + ghost listsO(1) amortizedWorkload swings between recency and frequencyMore complex to implement and tune
TinyLFUCache policy + frequency sketchO(1) expectedMany one-hit arrivals compete with a smaller hot working setNeeds approximate counting and more moving parts

Decision guide

  • Pick LRU when you want the simplest high-performance policy and the workload has strong short-term locality.
  • Pick LFU when long-term hot items matter more than the last few accesses.
  • Pick ARC when you want the cache to automatically rebalance between recency and frequency.
  • Pick TinyLFU when admission quality matters as much as eviction, especially in large service caches full of one-off traffic.

Key takeaways

  • Cache eviction is a real algorithm choice, not an afterthought on top of a hash table.
  • LRU = recency, LFU = frequency, ARC = adaptive recency/frequency, TinyLFU = admission-aware frequency filtering.
  • The classic LRU implementation is hash map + doubly linked list, which is why it shows up so often in interviews.
  • LFU needs more machinery because it must update both key lookup and frequency ordering on every hit.
  • TinyLFU explains why sketches like Count-Min Sketch matter: approximate counting can improve a very practical systems decision.

Practice problems

ProblemDifficultyKey idea
LRU CacheMediumHash map plus doubly linked list for O(1) get and put
LFU CacheHardFrequency buckets plus recency tie-breaking
ARC paperHardAdaptive recency vs. frequency
TinyLFU paperHardAdmission by approximate frequency instead of blind insertion

Relation to other topics

  • Hash Map gives the O(1) key lookup every practical cache implementation needs.
  • Linked List supports LRU-style recency ordering with constant-time remove and insert.
  • Count-Min Sketch often appears inside TinyLFU-style admission logic.
  • Consistent Hashing decides which machine owns a key, while cache eviction decides what survives inside one machine’s local cache.

Build on these first

These topics supply the mental model or underlying structure that this page assumes.

Related directions

These topics live nearby conceptually, even if they are not direct prerequisites.

More from Systems & storage

Stay in the same family when you want parallel variations of the same mental model.

Paths that include this topic

Follow one of these sequences if you want a guided next step instead of open-ended browsing.

Cache & distribution

Connect local eviction policy with distributed key placement and system-scale cache design.

From the blog

Pair the atlas with recent writing from the rest of the site when you want a broader engineering perspective alongside the topic graph.