TTL LRU LFU and Cache Memory Limits

Caching and Failure Recovery · 40 min

System Design · Module 6 · Lesson 2 of 4 · 40 min

TTL LRU LFU and Cache Memory Limits

Trace exact LRU by hand, distinguish age-based expiration from capacity eviction, and practice a namespaced Redis entry lifecycle.

See hits, misses and eviction →
Expiration bounds entry lifetime; eviction chooses what to remove when memory is needed, and neither alone guarantees fresh data.

01 The idea

The problem this lesson solves

A cache has finite memory. When new data no longer fits, the system must reject writes, remove entries or use another configured behavior. Separately, values may expire because they are too old under the cache policy. Eviction and expiration solve different problems. We will trace concrete reads through a tiny cache so the policies become visible rather than remaining acronyms.

You should recognize the application, the database and the request path. Treat the database as the authority unless explicitly stated otherwise.

TTLA configured remaining lifetime after which an entry expires.
LRULeast recently used: evict the entry with the oldest recent access under the policy.
LFULeast frequently used: prefer eviction based on observed access frequency under the implementation.

02 Worked example

Trace six reads through a two-entry cache

TTL LRU LFU and Cache Memory Limits worked diagram01A, BTwo initial misses02Read AOrder: B, A03Read CEvict B → A, C04Read A, B2 hits / 4 missesTTL LRU LFU and Cache Memory Limits compact diagram01A, BTwo initial misses02Read AOrder: B, A03Read CEvict B → A, C04Read A, B2 hits / 4 misses
Read order is A, B, A, C, A, B with capacity two. Cache order runs least to most recent: two hits, four misses.
1
A, then BBoth miss, so both are loaded from the database. Ordered least-recent to most-recent, the cache is [A,B].
2
Read AA hits and becomes most recent. The order is [B,A].
3
Read C, then AC misses and evicts B, giving [A,C]. A then hits and the order becomes [C,A].
4
Read BB misses and evicts C. The final order is [A,B]. There were 2 hits and 4 misses out of 6 reads, a 33.3% hit rate.

03 The method

Understand the mechanism

Separate expiration from eviction

Expiration removes or makes an entry unavailable after its lifetime ends. Eviction removes an entry to satisfy a capacity policy, even if its TTL has not elapsed. A value can disappear because of either event, manual deletion, restart or another operational action.

A TTL therefore does not promise that an entry will remain present for that duration. It sets an expiry boundary under the implementation’s behavior. Applications using a cache must tolerate earlier misses and recover from the authority.

Likewise, a long TTL does not guarantee fresh data. It may keep an old value eligible for reuse longer unless updates are coordinated. Memory management and freshness are related operationally but are not the same guarantee.

When diagnosing misses, distinguish expired entries, capacity evictions and absent keys that were never loaded. They suggest different remedies: changing expiry policy, increasing useful capacity or improving access patterns.

Trace exact LRU by hand

Least recently used, or LRU, removes the entry whose last access is oldest in the model. Use capacity two and requests A,B,A,C,A,B. Write cache order from least recent to most recent after every request.

A misses and gives [A]. B misses and gives [A,B]. A hits and moves to the most-recent end, giving [B,A]. C misses, evicts B and gives [A,C]. A hits and gives [C,A]. B misses, evicts C and gives [A,B].

There are two hits and four misses, for a hit ratio of 2/6 ≈ 33.3%. The final set alone does not reveal that history; you must trace accesses and updates to recency.

LRU exploits temporal locality: recently used data may be used again soon. A one-time scan over many distinct keys can evict a useful hot set, so recency is not always a good predictor. Actual cache products may approximate LRU to reduce bookkeeping cost; the exact toy model teaches the policy, not every implementation detail.

Compare FIFO and LFU on workload behavior

First in, first out removes the oldest inserted entry without moving it merely because it was read. In the same A,B,A,C sequence, reading A does not renew its position, so C can evict A. FIFO has different metadata and behavior from LRU.

Least frequently used, or LFU, favors entries accessed often. It can protect a repeatedly popular key from a stream of one-time reads. But frequency needs a time model: an entry popular last month should not necessarily dominate forever after the workload changes.

Implementations may use approximate counters, decay or sampling. Tie-breaking also matters in small examples. State the exact toy policy before calculating a hit count, and consult the selected product’s behavior before relying on a particular ordering.

Choose with representative traces rather than a universal ranking. A cache serving a stable hot set, a sequential scan and a rapidly changing trend can favor different policies. Memory cost and bookkeeping overhead also contribute.

Size entries and choose the eviction domain

Memory use includes key bytes, value bytes and structural overhead. Two caches with the same entry count can consume very different memory if one stores large JSON documents and the other stores small identifiers.

An eviction policy can apply to all keys or a configured subset, depending on the system. Mixing disposable cache entries with authoritative counters or session state in one eviction domain can remove data the application was not prepared to lose.

Decide what happens when memory is full and no eligible entry can be removed. Some configurations reject memory-growing writes rather than evicting. The application must handle that result instead of assuming every SET succeeds.

Monitor memory, evictions, rejected writes, hit ratio and origin load together. Increasing the memory limit can help if the working set barely exceeds capacity, but not if keys grow without bound because the application includes a random value in every key.

Use TTL commands as observations, not assumptions

In the Redis tutorial, SET with EX 60 stores a value with a sixty-second expiry. GET retrieves it while present. TTL reports remaining lifetime in seconds, with special values indicating missing keys or keys without expiry.

The remaining TTL changes as time passes, so a test should check a valid range rather than require exactly 60 at every observation. After DEL, GET returns a miss and TTL reports the missing-key sentinel. This gives you a concrete way to distinguish a value from its lifetime metadata.

Be careful when rewriting values or copying data between layers: the operation’s expiry behavior must be understood. A new write may require explicitly preserving or setting the intended TTL under that command’s semantics.

Expiration work and physical reclamation are implementation details; application correctness should depend on documented lookup semantics rather than guessing when a background cleanup loop frees memory. A cache miss remains a normal branch.

Tune using traces and recovery limits

Capture an appropriate access trace or generate a representative workload in a practice environment. Compare policies at several capacities while keeping the trace constant. Report hit ratio, origin work and memory rather than choosing a winner from one tiny example.

Include a working-set change. A policy that performs well during steady popularity may recover slowly when a new campaign starts. Include large values and cold scans if they exist in the real workload.

Eviction can create an origin surge. If many useful entries disappear together, misses increase even though external QPS is unchanged. The cache recovery lesson supplies coalescing and admission controls; eviction tuning alone cannot guarantee safe origin load.

Your design should explain which data is disposable, which policy selects victims, how expiration differs, and what the application does on a miss or rejected fill. Those statements are more durable than memorizing a product’s default setting.

Guided tutorial

  1. Trace LRU without skipping a read

    Write six rows for A,B,A,C,A,B with capacity two. Record hit or miss, evicted key if any, and least-to-most-recent order. Verify [A,B] at the end and exactly two hits. Compare with the interactive preset.

  2. Change the policy

    Trace FIFO on the same sequence, keeping insertion order unchanged on hits. Identify the first point at which its victim differs from LRU. Explain the workload assumption behind each policy rather than declaring one always better.

  3. Run the Redis expiry experiment

    Use a disposable Redis instance and the commands below. Observe the stored URL, a positive TTL, deletion and the final miss. A missing key gives TTL -2; an existing key without expiry gives -1. Do not treat either sentinel as a remaining number of seconds.

  4. Review the memory boundary

    Suppose the same instance holds disposable mappings and critical session records. Decide whether they should share an eviction domain. Describe the user-visible failure if a session is evicted and identify the authority or recovery behavior required by your chosen design.

Redis CLI commands

redis-cli SET sd:lab:link:a7 https://example.com EX 60
redis-cli GET sd:lab:link:a7
redis-cli TTL sd:lab:link:a7
redis-cli DEL sd:lab:link:a7
redis-cli GET sd:lab:link:a7
# Expected final result: nil (cache miss)
# TTL: -2 means missing; -1 means an existing key has no expiry.

Check your result

GET returns the stored URL before deletion and nil afterward. TTL is positive while the key remains unexpired, -2 when missing, and -1 only for an existing key without expiry.

Execute the exact LRU trace

OrderedDict makes the least-to-most-recent order explicit. This is an exact teaching model; a cache product may use an approximation.

from collections import OrderedDict
cache = OrderedDict()
hits = 0
for key in ["A", "B", "A", "C", "A", "B"]:
    hit = key in cache
    if hit:
        hits += 1
        cache.move_to_end(key)
    else:
        if len(cache) == 2:
            cache.popitem(last=False)
        cache[key] = True
    print(key, "hit" if hit else "miss", "".join(cache))
print(f"hits={hits}, misses={6-hits}")

Expected output

A miss A
B miss AB
A hit BA
C miss AC
A hit CA
B miss AB
hits=2, misses=4

A hit changes recency without increasing the entry count. Remove the move_to_end line to explore insertion-order eviction, then compare the victim and hit behavior.

05 Cheat sheet

Review the decisions

Use this table to explain the mechanism and its limit without memorizing a product name.

Choice or conceptRememberCheck
ExpirationLifetime endedCan occur with free memory
LRU evictionOldest recent useA hot stale value can stay
LFU evictionFrequency signalPolicy and decay details matter
Memory limitCapacity boundaryMiss pressure affects the origin

06 Where & why

Check the primary references

Use official references for protocol and implementation details. The questions here are teaching prompts.

Primary reference
Redis eviction policies

Read Redis eviction policies

Primary reference
Redis TTL semantics

Read Redis TTL semantics

Primary reference
Redis SET and expiration options

Read Redis SET and expiration options

State the implementation version and the workload assumptions when defending your own design.

07 Interview questions

Explain the reasoning out loud

Answer before opening the explanation. The questions progress from vocabulary to decisions and failure behavior.

What does TTL mean?
A configured remaining lifetime after which an entry expires.
What does LRU mean?
Least recently used: evict the entry with the oldest recent access under the policy.
What does LFU mean?
Least frequently used: prefer eviction based on observed access frequency under the implementation.
Does LRU guarantee freshness?
No. It tracks use rather than source updates. Frequently read old data can be recent and stale at the same time.
Can an unexpired key be removed?
Yes, an applicable capacity eviction policy can remove it to make room. The TTL is not a reservation of memory.
Does TTL report the source’s last modification time?
No. It reports the key’s remaining expiry lifetime. Source freshness is a separate property.
Are Redis LRU and the console identical algorithms?
The console uses exact LRU for a tiny trace. Redis policies can use approximate sampling and implementation-specific behavior.
Why inspect memory and origin together?
Eviction raises misses, which can increase database work. A cache-only metric can hide the downstream cost of a memory decision.

08 Practice problems

Practice a new case

Solve the new cases before revealing hints. Complete worked solutions follow the set.

Hit rate

Easy
Compute the hit rate for 18 hits and 6 misses.
Follow-up
Use total lookups as the denominator.
Show the hint
Hits plus misses equals the total.

Name the removal

Easy
Classify removing an entry after 30 seconds and removing an unexpired entry to free memory.
Follow-up
Both produce a later miss but have different causes.
Show the hint
Age and capacity are separate triggers.

LRU trace

Medium
Trace A,B,C,A,D with capacity 3 and list the evicted key and final least-to-most-recent order.
Follow-up
A hit changes recency.
Show the hint
Move A to the recent end before inserting D.

LRU capacity

Medium
For A,B,C,A with capacity two, count hits and show the final order.
Show the hint
A was evicted before its last request.

TTL interpretation

Medium
Interpret TTL values 12, -1 and -2.
Show the hint
Distinguish remaining life from no expiry and absence.

Eviction review

Hard
A cache’s eviction count rises after new metadata fields are added, while external QPS is unchanged; explain a test plan.
Show the hint
Entry size can change the effective working-set capacity.

Worked solutions

1. Hit rate

18 / (18 + 6) = 0.75, or 75 percent.

2. Name the removal

The first is expiration. The second is eviction. Neither alone tells you whether the backing value changed.

3. LRU trace

After A,B,C the order is [A,B,C]. A hits, producing [B,C,A]. D misses and evicts B. Final order: [C,A,D].

4. LRU capacity

All four requests miss. After C the order is [B,C]; the last A evicts B, leaving [C,A].

5. TTL interpretation

12 is about twelve seconds remaining; -1 means an existing key has no expiry; -2 means the key does not exist.

6. Eviction review

Measure representative entry memory, resident key count, hit/miss rate and origin load. Compare the old and new value shapes under the same workload and test memory/policy changes against the freshness and recovery contract.