Level 2 · 20 min
Cache Invalidation
Cache invalidation is famously one of the hardest problems in computer science. The challenge: keeping cached data fresh enough to be useful while not defeating the purpose of caching by invalidating too eagerly.
TTL-Based Expiry
Redis expires keys via two complementary mechanisms. Active expiry: a background timer controlled by the hz config (default 10 cycles/second) samples 20 random keys with TTLs per cycle and deletes expired ones. With hz=10 and millions of keys, an expired key can linger for up to 100ms after its TTL — in practice often much longer if only a small fraction of keys have TTLs. Lazy expiry: every key access checks TTL and deletes the key if expired before returning the result. The combined effect means actual eviction can lag nominal TTL by hz-dependent seconds. For session invalidation or compliance-driven deletion ("delete within 1 second of logout"), this lag matters — increase hz to 20 or use an explicit DEL at logout. PEXPIRE and PTTL provide millisecond-precision TTL management. OBJECT IDLETIME key returns seconds since last access — use this to identify cold keys for proactive manual eviction without relying solely on TTL.
Eviction Policies
Eviction policy mechanics: allkeys-lru uses an approximate LRU algorithm. Redis maintains a pool of 16 eviction candidates sampled at random; the candidate with the oldest last-access time is evicted. maxmemory-samples (default 5) controls how many random keys feed into each eviction decision — higher values give better LRU approximation at the cost of more CPU per eviction. allkeys-lfu tracks access frequency using a Morris counter (probabilistic 8-bit per key) that decays over time via lfu-decay-time (default 1 minute). LFU is superior for workloads with long-tail access patterns where recently-cold but historically-hot items should be retained over temporarily-hot items. volatile-lru and volatile-lfu only consider keys with TTLs set — if no keys have TTLs and the policy is volatile-*, Redis returns OOM errors instead of evicting. Active defragmentation: CONFIG SET activedefrag yes enables online defrag (Redis 4.0+, requires jemalloc allocator). Monitor active_defrag_running in INFO memory; set active-defrag-ignore-bytes 100mb and active-defrag-threshold-lower 10 to control aggressiveness. Production insight from Redis in Action: the default maxmemory-policy is noeviction — Redis returns OOM errors on write commands rather than evicting any key, which is the correct behavior for a primary data store but catastrophic for a pure cache; Carlson explicitly distinguishes these two Redis deployment modes, noting that cache-mode instances should always configure an explicit eviction policy. OBJECT ENCODING and OBJECT IDLETIME (for LRU) or OBJECT FREQ (for LFU) are diagnostic commands to inspect individual key metadata — run these on a sample of your hottest and coldest keys to validate that the eviction policy is behaving as intended before a memory pressure incident occurs.
Event-Driven Invalidation
Event-driven invalidation via CDC (Change Data Capture): Debezium tails PostgreSQL WAL and publishes row-level changes to Kafka. A cache invalidation consumer reads the Kafka topic and issues Redis DEL or UNLINK commands. End-to-end lag from DB commit to cache invalidation is typically 50-200ms — far better than relying on TTL alone. CDC catches DB changes from any source: migrations, admin tools, other microservices, not just your application writes. Versioned keys eliminate invalidation entirely: instead of product:1001, use product:1001:v3 where the version is stored in a separate key (product:1001:version). Readers fetch the current version first, then the versioned key. Old versioned keys accumulate — use TTLs on all versioned keys and a background sweeper. Cache tags maintain a secondary index: tag product:1001 as belonging to tag:category:electronics (a Redis Set). On category update, SMEMBERS tag:category:electronics → pipeline UNLINK all tagged keys. Requires maintaining the tag set on every write, but enables surgical bulk invalidation.
Code example
cache006.learn.codeExample