Expiration Strategy - TTL Design

Redis keys can have a TTL (time to live). When the TTL expires, the key is deleted. Setting TTLs well avoids unbounded memory growth, limits staleness in caches, and reduces the risk of cache avalanche when many keys expire at once. This article covers how expiration works, how to choose TTLs, and how to add jitter.

Overview

  • EXPIRE / PEXPIRE: Set seconds or milliseconds until the key is removed. SET with EX/PX sets the value and TTL in one command.
  • Expiration semantics: Redis deletes expired keys lazily (on access) and in a background cycle; so memory may not drop exactly at TTL. A key is considered expired when accessed and is not returned.
  • TTL choice: Long TTL for stable data (fewer DB hits, more staleness risk). Short TTL for volatile or “absent” placeholders. Always set a TTL for cache keys so cold or bad data does not stay forever.
  • Jitter: Add random offset to TTL so keys do not all expire at the same time (avoids thundering herd and cache avalanche).

Example

Example 1: Setting TTL on set

Redis
SET user:1001 "..." EX 3600
SET session:abc "..." PX 900000
EXPIRE cache:item:1 7200
TTL user:1001
  • EX 3600 = 3600 seconds; PX 900000 = 900000 ms. TTL returns remaining seconds (-1 = no TTL, -2 = key does not exist).

Example 2: TTL by use case

Use caseSuggested TTLReason
User profile cache5–60 minBalance freshness vs DB load
Session15–30 minSecurity and memory
“Absent” placeholder1–5 minAvoid penetration; short so real data can appear soon
Rate limit window1–60 sReset window; match limit granularity
Static reference data1–24 hRarely changes; long TTL OK with invalidation on update

Example 3: Jitter to avoid avalanche

Java
int baseTtl = 3600;
int jitter = ThreadLocalRandom.current().nextInt(0, 600);
redis.setex(key, baseTtl + jitter, value);  // 3600–4200 s
  • Keys loaded in the same batch will expire over a 10-minute window instead of at the same second, spreading load and avoiding cache stampede when they all expire.

Core Mechanism / Behavior

  • Lazy deletion: When a key is read, Redis checks expiry and deletes it if expired before returning. So expired keys can still occupy memory until touched or cleaned.
  • Active expiry: Redis runs an internal job that samples keys and deletes expired ones. With many keys and few accesses, memory can stay high until the sampler hits them.
  • Replication: Replicas do not expire keys independently; they wait for the primary’s DEL or the primary’s expiry propagation so behaviour is consistent.

Key Rules

  • Always set a TTL on cache keys (and “absent” placeholders) to avoid unbounded growth and to bound staleness.
  • Add jitter (e.g. base TTL + random 0–10%) when setting many keys at once so they do not all expire together.
  • Prefer EXPIRE/SET EX in application code for cache-aside; use a consistent policy (e.g. same base TTL and jitter range per key type) for predictable behaviour.

What's Next

See Cache-Aside Pattern and Caching Pitfalls for cache usage and avalanche. See Data Types for key design. See Redis Persistence for how RDB/AOF interact with expiry (expired keys are not written).