Maintenance

Site is under maintenance — quizzes are still available.

Go to quizzes

Choose Between allkeys-lru and volatile-ttl

Learn when to use allkeys-lru vs volatile-ttl eviction policies in Redis – pick the right strategy based on your cache's TTL discipline and memory constraints.

Focus: choose between allkeys-lru and volatile-ttl

Sponsored

Running out of memory in the middle of a production spike is every engineer's nightmare. Redis eviction policies determine exactly how the server frees memory when it runs low — and choosing the wrong one can lead to catastrophic cache misses, thundering herds, or total data loss. This lesson demystifies two popular eviction options — allkeys-lru and volatile-ttl — so you can pick the right one for your workload without guesswork.

The problem this lesson solves

Redis is not a data coffin — it's a cache, and caches have finite memory. When maxmemory is hit (default: no limit, change with CONFIG SET maxmemory 100mb), Redis must evict some keys to make room. The eviction policy you choose governs which keys are evicted. If you pick poorly:

  • Critical keys with a short time-to-live (TTL) may disappear prematurely.
  • Keys that should never be evicted (no TTL) can be removed, causing permanent data loss.
  • The eviction itself can cause latency spikes as Redis scans keys and decides what to remove.

Understanding the difference between allkeys-lru (evict any key based on last recent use) and volatile-ttl (evict only keys with a TTL, preferring those with the earliest expiration) is essential to designing a resilient cache. This lesson gives you the mental model, the code, and the trade-offs to make that choice confidently.

Core concept / mental model

Think of Redis as a busy airport parking lot. The lot has a fixed number of spaces (maxmemory).

  • allkeys-lru works like a valet who observes which cars have been parked longest without moving. If a car hasn't moved in a week, it gets towed (evicted) — regardless of whether the owner paid for a reserved spot (TTL). It treats every key equally, preferring to evict the least recently used ones.

  • volatile-ttl works like time-limited parking meters. Only cars with meters (keys with TTLs) can be towed. The valet evicts the cars whose meters are about to expire first — i.e., the key with the shortest remaining TTL. Keys without a TTL (no meter) are immune to eviction.

Pro tip: volatile-ttl is not the same as volatile-lru. volatile-lru evicts the least recently used among keys with TTLs, while volatile-ttl evicts the key with the nearest expiration time. They are different!

How it works step by step

Step 1: Enable a maxmemory limit

Redis won't evict at all unless maxmemory is set. By default it's 0 (no limit) in many configs but can be maxmemory 0 meaning unlimited in some versions. Always set a realistic limit for your cache workload.

redis-cli CONFIG SET maxmemory 50mb

Step 2: Choose an eviction policy

Set the eviction policy at runtime or in redis.conf. For this lesson we compare allkeys-lru and volatile-ttl.

redis-cli CONFIG SET maxmemory-policy allkeys-lru

Swap with volatile-ttl when you want to protect no-TTL keys.

Step 3: Understand the eviction algorithm

  • allkeys-lru: Redis maintains a pool of candidate keys (sampled randomly, default sample size = 5). From that pool, the least recently used key is evicted. This is approximate LRU, not perfect, but highly efficient.
  • volatile-ttl: Redis samples a small set of keys that have a TTL set. From that sample, the key with the shortest TTL remaining is evicted. Keys without TTL are never considered.

Step 4: Monitor evictions

You can see how many keys have been evicted using INFO stats.

redis-cli INFO stats | grep evicted

Output: evicted_keys:0 — this will increase as memory fills up.

Hands-on walkthrough

Let's create a test scenario. We'll fill Redis with keys, some with TTL, some without, then force eviction and observe which keys survive.

Setup: create keys with and without TTL

import redis

r = redis.Redis(decode_responses=True)
r.flushdb()

# Set maxmemory to a very low value (e.g., 1MB) to force eviction quickly
r.config_set("maxmemory", "1mb")

# Insert 10,000 keys: 5,000 with TTL, 5,000 without TTL
for i in range(10000):
    key = f"key:{i}"
    value = "x" * 200  # each value ~200 bytes
    if i < 5000:
        r.setex(key, 3600, value)  # 1 hour TTL
    else:
        r.set(key, value)  # no TTL

# Check current memory
info = r.info()
print(f"Used memory: {info['used_memory_human']}")
# The 'maxmemory' is 1MB, so Redis will evict keys soon

Test policy 1: allkeys-lru

r.config_set("maxmemory-policy", "allkeys-lru")

# Insert more keys to force eviction
for i in range(10000, 12000):
    r.set(f"newkey:{i}", "x" * 200)

# Check which keys remain (sample a few)
print("Surviving keys with TTL?")
print(r.ttl("key:0"))   # if -2, evicted; if still positive, survived
print(r.ttl("key:4999")) # same
print("Surviving keys without TTL?")
print(r.exists("key:5000"))  # 0 if evicted, 1 if alive

With allkeys-lru, both TTL and no-TTL keys can be evicted. Least recently used keys get evicted first.

Test policy 2: volatile-ttl

r.flushdb()
r.config_set("maxmemory", "1mb")
r.config_set("maxmemory-policy", "volatile-ttl")

# Re-insert the same 10,000 keys
for i in range(10000):
    key = f"key:{i}"
    value = "x" * 200
    if i < 5000:
        r.setex(key, 3600, value)
    else:
        r.set(key, value)

# Insert more keys
for i in range(10000, 12000):
    r.set(f"newkey:{i}", "x" * 200)

# Check which keys remain
print("Key without TTL still exists?")
print(r.exists("key:5000"))  # Should be 1 (not evicted)
print("TTL key with long remaining?")
print(r.ttl("key:0"))        # Likely still positive

Expected outcome: Under volatile-ttl, no-TTL keys (key:5000 and beyond) should always exist because they are never evicted. Only TTL keys will be removed, and Redis tries to evict those with the shortest TTL (e.g., keys TTL set to 1 second would be evicted before those with 3600 seconds).

Compare options / when to choose what

Scenario Recommended policy Reason
All your keys have TTLs; you want to evict expired-but-not-yet-deleted keys first volatile-ttl Evicts keys nearest to expiration, protecting those with longer TTL. Works well for session caches.
Your keys have mixed TTL/no-TTL, and no-TTL keys are critical (e.g., configuration data) volatile-ttl No-TTL keys are never evicted. Perfect for values that should stay in memory forever.
All keys are equally important; you want a simple “least recently used” eviction allkeys-lru Fair across all keys regardless of TTL. Good for generic LRU caches.
Some keys have TTL, some don't, but you don't want to guarantee persistence for no-TTL keys allkeys-lru If memory is tight and all keys are evictable, you get more eviction capacity. Use this if no-TTL keys are also cache data.
You want to evict only keys that are about to expire to maximize their remaining lifespan volatile-ttl Prefers eviction candidates with the shortest remaining TTL, so keys with longer TTL stay longer.

Other policies like allkeys-random and volatile-lru exist but are out of scope here.

Troubleshooting & edge cases

Problem 1: Using volatile-ttl with no TTL keys but expecting eviction

r.config_set("maxmemory-policy", "volatile-ttl")
for i in range(100000):
    r.set(f"key:{i}", "x" * 200)  # no TTL
# Memory fills up, but no eviction occurs — Redis returns OOM error!

Fix: Either set TTLs on some keys, or change policy to allkeys-lru for this dataset.

Problem 2: volatile-ttl still evicts keys you thought had long TTL

If many keys have identical TTL values (e.g., all set to 3600 seconds), Redis picks the key that appears first in its random sample — leading to seemingly random eviction. To bias toward truly short-TTL keys, use TTLs with more variation.

Problem 3: maxmemory-policy not taking effect

Ensure you've set maxmemory to a nonzero value. Without it, no eviction occurs.

redis-cli CONFIG SET maxmemory 1gb
redis-cli CONFIG SET maxmemory-policy allkeys-lru

What you learned & what's next

You learned the core difference between allkeys-lru and volatile-ttl: the former evicts any key based on least recent use; the latter evicts only TTL keys based on shortest remaining TTL. You now understand how to choose based on your data's TTL discipline and memory constraints.

Next lesson: Learn how to combine eviction policies with key expiration strategies — using EXPIRE, TTL, and proactive eviction to keep your cache warm and your database cold.

Practice recap

Try this: set up a small test with 10% of your keys having a very short TTL (e.g., 10 seconds) and 90% having a long TTL (3600 seconds). Run with volatile-ttl and observe which keys get evicted first — you'll see the short-TTL keys disappear as maxmemory is approached. Then switch to allkeys-lru and repeat — note how the eviction pattern changes, and which key type survives.

Common mistakes

  • Using volatile-ttl on a dataset where no keys have a TTL — Redis will stop accepting writes when maxmemory is reached; eviction never fires. Always ensure at least some TTLed keys exist.
  • Assuming volatile-ttl evicts the least recently used TTL key — it actually evicts the key with the shortest TTL remaining, not the oldest access time. Use volatile-lru for that.
  • Setting identical TTLs on all TTL keys under volatile-ttl — since all have the same remaining time, eviction becomes random among them, defeating the purpose. Add jitter to TTL values.

Variations

  1. Use allkeys-random when you don't care about which key is evicted — simpler but less predictable than LRU.
  2. Use volatile-lru if you need an LRU approach but only among keys that have a TTL — protects no-TTL keys like volatile-ttl but uses LFU-like eviction.
  3. Use noeviction policy if you'd rather return errors on writes than evict any data — for strict data retention, but high risk of write failures.

Real-world use cases

  • Session store with configurable TTL: Use volatile-ttl to evict expiring sessions first, while permanently cached user profiles (no TTL) remain untouched.
  • General-purpose API cache storing both short-lived (TTL) and long-lived cache keys: allkeys-lru keeps the hottest data regardless of TTL, which is ideal when all keys are cache-like.
  • Rate limiter counters with TTL: Use volatile-ttl to evict old rate-limit buckets by TTL, while keeping persistent config keys (no TTL) safe from eviction.

Key takeaways

  • Set maxmemory to a value that matches your instance's RAM — Redis will evict only when this limit is reached.
  • allkeys-lru evicts any key based on approximate least-recently-used algorithm; good for generic caches where all keys are equally cacheable.
  • volatile-ttl evicts only keys that have a TTL, and prefers evicting the key with the shortest remaining TTL; protects no-TTL keys perfectly.
  • If you mix TTL and no-TTL data, volatile-ttl guarantees no-TTL keys are never evicted — use for session caches with long-lived config or tokens.
  • Always monitor evicted_keys and keyspace_hits/misses after changing policy — a spike in evictions may indicate your maxmemory is too low or policy is wrong.

Sponsored

Sponsored

Discussion

Questions, corrections, and tips help everyone reading this page.

0 comments

Add a comment

Shown publicly with your comment.

Be constructive · max 4,000 characters

No comments yet — start the thread.

Related tutorials, quizzes, and articles for this topic.