Interview Prep Hub

Redis & Caching

You added Redis caching to cut latency by 40%. Expect questions on cache patterns, eviction, persistence, and pitfalls.

Redis Architecture

Redis is an in-memory data structure store. It is primarily single-threaded for command execution. This means commands are executed sequentially, guaranteeing atomicity and eliminating the need for internal locks. (Note: Redis 6+ introduced I/O threads for network parsing, but command execution remains single-threaded).

Data Structures

StringHashList SetSorted SetStream cache, counters, flagsINCR is atomic — rate limits an object by fieldupdate one field, no re-serialise queue / stackLPUSH + BRPOP = blocking queue unique membershipSINTER for mutual follows LEADERBOARDSZADD / ZREVRANGE — O(log n) rank append-only logconsumer groups, replayable Redis runs commands on one thread — every op is atomic, and one O(n) call blocks all. Never run KEYS * in production. Use SCAN — it is cursor-based and does not block. Same rule for large DEL, FLUSHALL, and any O(n) range over a huge collection.
Picking the right structure is most of Redis. Each one exists because a specific access pattern is O(1) or O(log n) on it.

Redis is not just a key-value store; it's a data structure server.

TypeCommandsUse case
StringGET, SET, INCRCached JSON blobs, counters, session tokens, rate limits. (Max 512MB)
HashHGET, HSET, HGETALLStoring object fields. Fetching specific user properties without parsing full JSON.
ListLPUSH, RPOP, LTRIMMessage Queues, maintaining a recent N items list (e.g. latest 100 comments).
SetSADD, SISMEMBER, SINTERUnique items, fast membership check. Tags, friends in common (intersection).
Sorted Set (ZSet)ZADD, ZRANGE, ZRANKLeaderboards, time-ordered streams, rate limiter sliding windows. Each item has a score.
StreamXADD, XREADGROUPAppend-only log with consumer groups — Kafka-lite for event sourcing.
HyperLogLogPFADD, PFCOUNTApproximate unique counts (e.g., millions of unique IP visitors) using only 12KB memory.
BitmapSETBIT, GETBITDaily active user flags, feature flags. Extremely memory efficient.
GeoGEOADD, GEORADIUSLocation-based queries, finding nearby drivers or stores.

Caching Patterns

Rendering diagram…

1. Cache-Aside (Lazy Loading) — your pattern

The application is responsible for reading and writing to both storage and cache. Best for general read-heavy workloads.

func GetEvent(ctx context.Context, id string) (*Event, error) {
    // 1. Try cache
    if data, err := redis.Get(ctx, "event:"+id).Result(); err == nil {
        var e Event; json.Unmarshal([]byte(data), &e); return &e, nil
    }
    // 2. Cache miss — hit DB
    e, err := db.GetEvent(ctx, id)
    if err != nil { return nil, err }
    
    // 3. Write back to cache with TTL
    data, _ := json.Marshal(e)
    redis.Set(ctx, "event:"+id, data, 60*time.Second)
    return e, nil
}

2. Write-Through

App writes data to cache and DB synchronously in one transaction. Pro: Data is never stale. Con: Slower writes.

3. Write-Behind (Write-Back)

App writes to cache only and returns immediately. An asynchronous process later flushes the cache to the DB. Pro: Extremely fast writes. Con: Risk of data loss if cache crashes before flush.

4. Read-Through

The application asks the cache for data. If missing, a cache provider automatically fetches it from the DB. Less app code.

TTL & Eviction Policies

Memory is expensive. You must manage how keys are removed.

  • TTL (Time To Live): Always set a TTL on cached data (SET key val EX 60). Never let caches grow unbounded.
  • maxmemory: The memory limit configured in redis.conf.
  • maxmemory-policy: What Redis does when it hits the limit:
    • allkeys-lru: Evict least recently used keys out of all keys. (Standard for caches).
    • volatile-lru: Evict least recently used keys among those with an expiration set.
    • allkeys-lfu: Evict least frequently used keys.
    • noeviction: Return errors on write. (Standard if Redis is used as a primary database/queue).

Common Caching Pitfalls

Rendering diagram…

  • Cache Stampede (Thundering Herd): A hot key (e.g., homepage data) expires. Suddenly 1,000 requests hit the DB at once, bringing it down.
    Solution: Use a Mutex (single-flight) so only one request queries the DB while others wait, or use probabilistic early expiration (refresh slightly before expiry).
  • Cache Penetration: Malicious users query for non-existent IDs. Cache misses, hits DB, returns nothing. Repeat.
    Solution: Cache the empty result (NULL) with a short TTL, or use a Bloom Filter before querying the DB.
  • Avalanche: All cache keys expire at the exact same time (e.g. nightly cron jobs), flooding the DB.
    Solution: Add random Jitter to TTLs (e.g., base TTL 60s + random(0-10s)).
  • Inconsistency on writes: If you update DB then update Cache, a crash between leaves a stale cache forever.
    Solution: Use Delete-on-write (invalidate cache). It's safer to delete the cache and let the next read repopulate it than to try to keep it perfectly updated.
  • Big Keys: Storing 50MB in one key blocks Redis (since it's single threaded) during serialization.
    Solution: Split data, or store large JSON/Images in S3 and cache the URL.
  • Blocking commands: Running KEYS * in production blocks all other commands.
    Solution: Use SCAN which iterates in chunks.

Distributed Redis & Concurrency

Distributed Locks

When multiple microservices need exclusive access to a resource.

// Acquire Lock (Set if Not eXists, with Expiry to prevent permanent deadlock)
SET lock:resource123 "unique_worker_id" NX EX 30

// Release Lock (Must check ownership first to avoid deleting another worker's lock)
// Handled via Lua script for atomicity:
if redis.call("get", KEYS[1]) == ARGV[1] then
    return redis.call("del", KEYS[1])
else
    return 0
end

Note: For highly critical locks across multiple nodes, the Redlock algorithm is used, though it has complex distributed systems caveats.

Pub/Sub vs Streams

  • Pub/Sub (PUBLISH/SUBSCRIBE): Fire and forget. If a subscriber is offline, the message is lost. Great for live real-time notifications.
  • Streams (XADD/XREAD): Persistent append-only logs. Consumer groups track offsets. If a worker dies, another can resume. Similar to Kafka.

Persistence (RDB vs AOF)

If Redis crashes, does it lose data?

  • RDB (Redis Database Snapshot): Point-in-time snapshot to disk every X minutes. Fast to restart, but loses recent data on crash.
  • AOF (Append Only File): Logs every write operation. Slower, larger file, but no data loss. Usually, production runs both.

High Availability

  • Redis Sentinel: Monitors master and replicas. Automatically promotes a replica to master if the master goes down.
  • Redis Cluster: Automatically shards/partitions data across multiple masters. Used when data is larger than RAM on a single machine.

Redis vs Memcached

FeatureRedisMemcached
Data typesStrings, Hashes, Lists, Sets, ZSets, StreamsStrings only
PersistenceYes (RDB/AOF)No (restarts empty)
FeaturesPub/Sub, Lua scripting, TransactionsBare-bones caching
ThreadingSingle-threaded executionMulti-threaded
Best forComplex data manipulation, queues, state, HAPure massive-scale string caching

Interview Quick Reference

TopicKey Points to Mention
Data TypesZSets for leaderboards, HyperLogLog for counting, Hashes for objects.
Cache-asideCheck cache → hit DB on miss → populate cache. Delete cache on write.
PitfallsStampede (thundering herd), Penetration (cache NULLs), Avalanche (add Jitter).
Evictionallkeys-lru, always set TTLs.
Single ThreadedNo race conditions on basic commands, but long operations (KEYS *) block the world.