Memcached TTL strategy: Set expiration for every key

Memcached doesn’t actually expire keys; it evicts them when it needs space.

Let’s see it in action. Imagine you have a users table and want to cache user profiles.

import memcache
import time

mc = memcache.Client(['127.0.0.1:11211'], debug=0)

user_id = 12345
user_data = {'name': 'Alice', 'email': 'alice@example.com', 'last_login': time.time()}

# Set the key with a 60-second expiration
mc.set(f"user:{user_id}", user_data, time=60)
print(f"Set user:{user_id} with expiration in 60 seconds.")

# Retrieve the key immediately
retrieved_data = mc.get(f"user:{user_id}")
print(f"Retrieved data immediately: {retrieved_data}")

# Wait for 61 seconds
print("Waiting for 61 seconds...")
time.sleep(61)

# Try to retrieve the key again
retrieved_data_after_expiration = mc.get(f"user:{user_id}")
print(f"Retrieved data after expiration: {retrieved_data_after_expiration}")

When you run this, you’ll see the data is retrieved immediately, but after 61 seconds, mc.get will return None. This looks like expiration, but it’s not quite what you might expect.

The core problem Memcached solves is reducing database load by keeping frequently accessed data in fast memory. When you use mc.set(key, value, time=ttl), you’re not telling Memcached to delete the key after ttl seconds. Instead, you’re providing a hint. Memcached uses this time value to calculate an expiration timestamp. When the current time exceeds this expiration timestamp, the key is marked as expired.

However, Memcached is a cache, not a persistent store. Its primary goal is to serve requests quickly. It has a fixed amount of memory, and when that memory fills up, it needs to make space for new items. Memcached uses a Least Recently Used (LRU) eviction policy. When it needs to evict an item to make room for a new one, it picks an item that hasn’t been accessed recently. If a key has expired and is also the least recently used item when memory is full, it will be evicted. If an expired key is still frequently accessed, it might not be evicted immediately, and mc.get will still return the data until Memcached needs the space. This is a crucial distinction: expiration is a soft deadline, and LRU eviction is the hard mechanism for memory management.

The time parameter you pass to set (or add, replace) is interpreted as a Unix timestamp if it’s greater than a certain threshold (usually 30 days, or 2592000 seconds). Otherwise, it’s treated as a relative number of seconds from the current time. So, time=60 means "expire 60 seconds from now." time=3000000000 (which is roughly 95 years from now) would be treated as a specific future timestamp. This can be a source of confusion if you’re not careful.

This soft expiration and LRU eviction means you can’t rely on Memcached to automatically remove stale data if your cache is constantly under pressure. If you have data that must be removed after a certain period, you need a strategy beyond just setting a TTL. This might involve:

  1. Application-level TTL management: Periodically scan for keys that should have expired and delete them using mc.delete(). This is inefficient for large caches.
  2. Cache invalidation signals: When the underlying data changes (e.g., a user profile is updated), explicitly delete the corresponding cache key. This is often the most robust approach for data that changes.
  3. Using a different caching solution: For caches where strict, guaranteed expiration is a primary requirement, solutions like Redis with its built-in EXPIRE command offer more predictable behavior.

The actual mechanism Memcached uses for eviction when memory is full is a probabilistic LRU. It doesn’t always pick the absolute least recently used item; instead, it samples a few items and evicts the least recently used among the samples. This is a performance optimization to avoid the overhead of constantly tracking exact access times for every single item.

The next concept you’ll run into is how Memcached handles item sizes and how to optimize your data serialization for network transfer and memory usage.

Want structured learning?

Take the full Memcached course →