Caching and Redis cheat sheet
The 12 Caching and Redis interview answers in 30 seconds each, plus quick facts. Printable, free.
One page per topic: print it or save it as PDF from your browser's print menu. Practise these questions
Rules of thumb
- Cache-aside: read cache → DB → fill with TTL; write DB → delete key after commit.
- Hot key: single-flight lock with expiry, or stale-while-revalidate.
- Many keys written together: jitter the TTL.
- Pure cache: maxmemory + allkeys-lru or allkeys-lfu.
- Personal responses: Cache-Control private or no-store.
- Cache misses too (short negative TTL); Bloom filter for random ids.
- Redis loses writes on failover; don't keep the only copy of money there.
Cache-aside: the default caching pattern
The application owns the logic: read from the cache; on a miss, read from the database, store the result with a TTL, and return it. On writes, update the database and then delete the cache entry so the next read reloads it. It's simple and the cache holds only what's read. The costs: the first read after expiry is slow, concurrent misses can stampede the database, and a race between a read and a write can leave a stale value until the TTL.
Write-through, write-behind and write-around
Write-through writes to the cache and the database in the same operation, so the cache is always warm for recently written data, at the cost of slower writes and caching data that may never be read. Write-behind writes to the cache and flushes to the database later, giving fast writes but risking data loss if the cache fails before flushing. Write-around writes only to the database and lets reads fill the cache, which avoids polluting it with write-once data.
Keeping a cache consistent with the database
Common causes: the cache was updated before the database transaction committed (and then it rolled back), or a slow reader loaded the old row, the writer deleted the key, and the reader then stored its stale copy. Fixes: delete the key after commit, keep TTLs short enough to bound staleness, and for stricter needs delete again shortly after (delayed double delete) or drive invalidation from the database's change stream so every writer is covered.
Cache stampede on a hot key
When a hot key expires, every request arriving before someone refills it misses and goes to the database at once: a stampede. Two fixes. Single-flight: only the request that wins a short lock reloads, and the others wait briefly for the new value. Stale-while-revalidate: keep serving the expired value while one background refresh runs, so nobody waits. In our Redis run, 50 requests over 600 ms made 30 database reads with no protection and 1 with either fix.
Spreading expiries with TTL jitter
They all expire in the same second, so the next minute starts with 1,000 misses hitting the database together. That's a different problem from one hot key: many keys share an expiry time. Add jitter: give each key a TTL with a random spread, for example 55 to 65 seconds. In our Redis run, the busiest second dropped from 1,000 expiries to about a tenth of that.
Eviction: LRU, LFU and maxmemory
With maxmemory set, Redis applies maxmemory-policy. allkeys-lru evicts the least recently used keys, a good default for a pure cache. allkeys-lfu evicts the least frequently used, better when a few keys stay popular over time. The volatile-* variants only evict keys with a TTL, and noeviction (the default) returns errors on writes, which suits Redis used as a primary store.
HTTP caching headers
Cache-Control: max-age=60 lets any cache reuse the response for 60 seconds. s-maxage overrides it for shared caches like CDNs. private allows only the user's browser to cache it, which you need for personalised responses. no-cache means store it but revalidate every time (with ETag), while no-store means never store it. stale-while-revalidate=30 lets caches serve a stale copy while they refresh in the background.
What to put behind a CDN
A CDN caches responses at edge locations near users. Static assets are the easy win; public, identical-for-everyone API responses (catalogue pages, config) can be cached with s-maxage and short TTLs. Personalised responses shouldn't be shared-cached. For updates, either rely on short TTLs plus stale-while-revalidate, use versioned URLs for assets, or purge by URL or tag through the CDN's API when content changes.
Choosing Redis data structures
Leaderboard: a sorted set (ZADD, ZRANGE … REV) keeps members ordered by score. Rate limiter: a string counter with INCR and EXPIRE, or a hash plus Lua for a token bucket. Session: a hash per session with a TTL. Simple queue: a list (LPUSH, BRPOP) or, with consumer groups and acknowledgement, a stream (XADD, XREADGROUP). Unique visitors: a HyperLogLog (PFADD, PFCOUNT) counts distinct items in about 12 KB with roughly 0.81% standard error.
Scaling a cache cluster and hot keys
Shard keys across nodes: Redis Cluster splits the keyspace into 16,384 hash slots spread over primaries, and clients route each key to its slot; client-side consistent hashing does the same for independent nodes. Add replicas to scale reads. A single hot key still lands on one node, so replicate it under several key names and pick one at random, or keep a short-lived in-process copy in each app instance.
Cache penetration and negative caching
Cache the absence: store a short-lived 'not found' marker for missing ids, so repeated lookups for the same id stop at the cache. For random ids that never repeat, put a Bloom filter of existing ids in front: it says 'definitely not present' with no false negatives, so you skip the database for most invalid ids. Add input validation and rate limits per client too.
Redis durability: RDB, AOF and when Redis is a database
RDB writes point-in-time snapshots; restarts lose everything since the last snapshot. AOF logs every write; with appendfsync everysec (the usual setting) a crash loses up to about a second, with always almost nothing at a large throughput cost. Replication is asynchronous, so a failover can drop acknowledged writes. Redis can be a primary store for data that tolerates that, but money and orders belong in a database with synchronous durability.
Quick facts
- Redis Cluster slots
- 16,384 hash slots, spread across primaries; hash tags {…} keep related keys in one slot.
- HyperLogLog
- Counts distinct items in about 12 KB with roughly 0.81% standard error.
- Default maxmemory-policy
- noeviction: writes fail when memory is full. Pure caches usually want allkeys-lru or allkeys-lfu.
- no-cache vs no-store
- no-cache: store but revalidate before use. no-store: never store.
- TTL jitter in our run
- 1,000 keys with a fixed 60 s TTL expired in one second; a 55-65 s spread cut the busiest second to about a tenth (Redis 7.4.6, EXPIRETIME).
- appendfsync everysec
- Redis's usual AOF setting: a crash loses up to about one second of writes.