Redis is the standard distributed cache for cloud-native applications. The patterns for using Redis correctly, and the anti-patterns that cause production problems, are worth documenting.
Cache-aside as the baseline pattern
Cache-aside (lazy loading): the application checks the cache on read; if a miss, it reads from the database and writes to the cache with a TTL. On write, the application updates the database and either invalidates the cache key or updates it. Cache-aside is simple, allows fine-grained TTL control, and is tolerant of cache failure (falls back to database on cache miss). The trade-off: the first request after a cache miss always hits the database.
Write-through for strong consistency
Write-through: writes go to both the cache and the database synchronously. The cache is always current; cache misses only occur for new data. The trade-off: write latency includes both database and cache write time. Write-through is appropriate when stale reads are unacceptable: user account data, permission records, and other consistency-sensitive data. Combined with cache-aside for reads, write-through provides a consistent cache at the cost of slightly higher write latency.
Redis data structure selection
Redis provides multiple data structures: strings (simple key-value), hashes (field-value maps, use instead of serialising an object to a string), lists (ordered collections, useful for queues and activity feeds), sorted sets (scores + members, ideal for leaderboards and time-series queries), sets (unique member collections, useful for unique visitor counting). Choosing the right data structure for the access pattern reduces memory usage and improves performance.
Cache stampede prevention
A cache stampede occurs when multiple concurrent requests all experience a cache miss simultaneously, all query the database, and all attempt to repopulate the cache. Under high load this can overwhelm the database. Prevention strategies: probabilistic early expiration (extend TTL occasionally before it expires to prevent mass expiry), locking (use a distributed lock during cache population so only one request populates the cache while others wait), and pre-warming (populate the cache before TTL expiry for high-traffic keys).