Ch. 27

Redis interview questions & answers

Redis for system design and backend interviews: data structures, caching patterns and eviction, TTLs, persistence, replication, Cluster, locks and rate limiting.

30 interview questions20 quiz questions8 notes
your progress0%

Notes in this chapter

Filter all notes →

30 Redis interview questions study by subtopic

30 questions
  1. 1.What is Redis, what is it used for, and how does it differ from Memcached?easy

    Redis is an in-memory data structure server. Keys map to typed values (strings, hashes, lists, sets, sorted sets, streams and more), and commands operate on those structures server-side, so INCR, ZADD or LPUSH are atomic without any client locking.

    Typical uses are caching, sessions, rate limiting, leaderboards, distributed locks, queues and lightweight messaging.

    Compared with Memcached:

    • Memcached stores opaque strings only; Redis has rich types and server-side operations.
    • Redis can persist to disk (RDB snapshots, AOF log), replicate, fail over with Sentinel and shard with Redis Cluster. Memcached is a pure volatile cache.
    • Memcached is multi-threaded for command execution; Redis executes commands on one main thread, with optional I/O threads.

    Since 2024 Redis is source-available (RSALv2/SSPLv1, with AGPLv3 added in Redis 8), and the BSD-licensed Valkey fork exists under the Linux Foundation.

    What interviewers listen for
    • In-memory server of typed data structures
    • Server-side atomic operations on those types
    • Optional persistence, replication and clustering
    • Memcached is a simpler, multi-threaded string cache

    Likely follow-up: When would you still pick Memcached? · Can Redis be your primary database?

  2. 2.Redis is often called single-threaded. Why is it still so fast, and what does io-threads change?mid

    Commands are executed by one main thread running an event loop on top of epoll/kqueue. It is fast because data lives in memory, the data structures are compact and well-chosen, and a single executor needs no locks or context switches. One thread can serve on the order of 100,000+ simple operations per second; the usual bottleneck is network and syscalls, not CPU.

    Redis is not literally one thread, though:

    • Background threads handle lazy freeing (UNLINK), AOF fsync and closing files.
    • fork() creates a child process for RDB snapshots and AOF rewrites.
    • With io-threads N (Redis 6+, reworked in Redis 8), extra threads read sockets, parse requests and write replies, while command execution stays serial.

    The consequence for design: every command runs alone, which makes single commands atomic, but one slow command (KEYS *, a huge DEL, a long Lua script) blocks every client.

    What interviewers listen for
    • One main thread executes commands serially
    • Speed comes from memory, data structures and no locking
    • I/O threads parallelize socket work, not execution
    • A slow command blocks every other client

    Likely follow-up: How would you use more CPU cores on one machine? · Which commands are dangerous on a large production instance?

  3. 3.Walk through the main Redis data types and a typical use case for each.easy
    • String: bytes up to 512 MB. Cached JSON, counters with INCR, flags, locks with SET NX.
    • Hash: field-value map under one key. A user profile or a shopping cart; update one field with HSET.
    • List: linked sequence with O(1) push/pop at both ends. Simple queues (LPUSH + BRPOP), recent-activity feeds trimmed with LTRIM.
    • Set: unordered unique members. Tags, "who liked this", intersections with SINTER.
    • Sorted set: unique members ordered by a score. Leaderboards, priority queues, time-indexed data, sliding-window rate limits.
    • Stream: append-only log with IDs and consumer groups. Event pipelines and durable job queues.
    • HyperLogLog: approximate distinct counts in at most 12 KB.
    • Bitmaps: bit operations on strings, such as daily active flags per user ID.
    • Geospatial: sorted sets with geohash scores for radius queries.

    Redis 8 also ships JSON, the Query Engine (secondary indexes and full-text search), time series, probabilistic types and vector sets in the core distribution.

    What interviewers listen for
    • Pick the type whose commands match the access pattern
    • Hashes for objects, sorted sets for rankings and time
    • Streams for durable messaging, lists for simple queues
    • HyperLogLog and bitmaps for compact counting

    Likely follow-up: How would you model a "top 10 this week" feature? · Why might you store an object as a hash rather than a JSON string?

  4. 4.Design a real-time game leaderboard with Redis. What commands do you use and what do they cost?mid

    A sorted set is the natural fit: member = player ID, score = points. Internally it is a skip list plus a hash table (or a compact listpack when small), so it supports both lookups by member and ordered range queries.

    • Award points: ZINCRBY lb:global 50 player:42, O(log N), atomic.
    • Top 10: ZRANGE lb:global 0 9 REV WITHSCORES, O(log N + 10).
    • A player's rank: ZREVRANK lb:global player:42, O(log N), zero-based.
    • Neighbours: take the rank, then ZRANGE a window around it.

    Store display names in a hash per player so the sorted set holds only IDs. For weekly boards, use a key per week (lb:2026-w41) with an expiry, and combine periods with ZUNIONSTORE. Ties are ordered by member name lexicographically, so if you need "first to reach the score wins", encode a time component into the score.

    What interviewers listen for
    • Sorted set with player ID and score
    • ZINCRBY, ZRANGE ... REV, ZREVRANK are all O(log N)
    • Separate keys per period with expiry
    • Ties break by member name unless encoded in the score

    Likely follow-up: How would you shard a leaderboard with 500 million players? · How do you show a percentile instead of an exact rank?

  5. 5.Should you store a user object as a JSON string, a hash, or with the JSON data type?mid

    It depends on how you read and write it.

    • JSON string (SET user:42 '{...}'): simplest, one round trip to read everything, works with any cache library. But updating one field means read, modify, write, which races between clients unless you use WATCH or Lua.
    • Hash (HSET user:42 name Ana plan pro): fields are updated independently and atomically (HINCRBY user:42 logins 1), and you can fetch only the fields you need with HMGET. Small hashes use the compact listpack encoding, so they are memory-efficient. Hashes are flat: nested data must be serialized per field. Redis 7.4 added per-field expiry (HEXPIRE).
    • JSON type (JSON.SET, built into Redis 8, a module before): nested documents with path-level updates like JSON.NUMINCRBY user:42 $.stats.logins 1, and indexable by the Query Engine.

    For a read-mostly cache blob, a string is fine. For objects with frequent partial updates, a hash. For nested documents you query, JSON.

    What interviewers listen for
    • Strings: whole-object reads, racy partial updates
    • Hashes: atomic per-field updates, compact when small
    • JSON type: nested paths and indexing
    • Choose by access pattern, not habit

    Likely follow-up: What is the race when two services update different fields of a JSON string?

  6. 6.How would you count daily unique visitors for millions of users without storing every ID in a set?mid

    Two options, depending on whether you need exactness.

    HyperLogLog gives an approximate distinct count with a standard error of about 0.81%, using at most 12 KB per key no matter how many items you add. PFADD uv:2026-10-07 user:42 records a visit, PFCOUNT uv:2026-10-07 estimates the count, and PFMERGE or PFCOUNT over several keys gives the weekly unique count. You cannot list members or test membership.

    Bitmaps are exact when user IDs are dense integers: SETBIT active:2026-10-07 42 1, then BITCOUNT for the total. Ten million users need about 1.2 MB per day, and BITOP AND across days answers "active every day this week". Sparse or huge IDs waste memory, because the string grows to the highest offset set.

    A plain set is exact and supports membership, but costs tens of bytes per member.

    What interviewers listen for
    • HyperLogLog: about 0.81% error, at most 12 KB
    • PFMERGE for unions across days
    • Bitmaps are exact for dense integer IDs
    • Sets are exact but memory-hungry

    Likely follow-up: Why is BITOP on two huge bitmaps a latency risk? · What does a Bloom filter answer that HyperLogLog cannot?

  7. 7.How does key expiry work in Redis? When is an expired key actually removed?easy

    You set a TTL with EXPIRE/PEXPIRE, SET key value EX 60, or an absolute time with EXPIREAT. TTL returns the remaining seconds, -1 if the key has no expiry and -2 if it does not exist.

    Removal uses two mechanisms:

    • Lazy (passive): when a client touches a key, Redis checks its deadline and deletes it if it has passed, so a client never sees an expired value.
    • Active: a background cycle (driven by hz, 10 times a second by default) samples keys that have a TTL and deletes the expired ones, repeating while a large share of the sample was expired. Memory from keys nobody reads is reclaimed this way.

    Gotchas: a plain SET on an existing key removes its TTL unless you pass KEEPTTL; INCR, HSET or LPUSH keep it. Replicas do not expire keys on their own; they wait for the primary's DEL, but they already report logically expired keys as missing.

    What interviewers listen for
    • Lazy deletion on access plus an active sampling cycle
    • TTL returns -1 (no expiry) or -2 (missing)
    • Plain SET clears the TTL; KEEPTTL preserves it
    • Replicas wait for the primary to expire keys

    Likely follow-up: Why can memory stay high after millions of keys have expired? · How do you expire a single field of a hash?

  8. 8.What happens when Redis reaches maxmemory? Compare the eviction policies.mid

    When a write would exceed maxmemory, Redis applies maxmemory-policy:

    • noeviction (the default): writes fail with an OOM error, reads still work.
    • allkeys-lru / allkeys-lfu: evict the least recently / least frequently used key among all keys. The usual choice for a pure cache.
    • volatile-lru / volatile-lfu: same, but only keys with a TTL. Useful when one instance mixes cache keys (with TTL) and data that must stay (without).
    • allkeys-random / volatile-random: random choice.
    • volatile-ttl: evict keys with the shortest remaining TTL first.

    If a volatile-* policy finds no key with an expiry, it behaves like noeviction and writes fail. Eviction is approximate: Redis samples maxmemory-samples keys (5 by default) and keeps a small pool of good candidates, rather than tracking an exact LRU list. Also leave headroom for replication buffers and copy-on-write during forks; maxmemory is not the process's peak memory.

    What interviewers listen for
    • Default noeviction rejects writes with OOM
    • allkeys-* for caches, volatile-* for mixed data
    • volatile-* with no TTL keys behaves like noeviction
    • Eviction is sampled and approximate

    Likely follow-up: How do you notice that eviction is hurting your hit rate? · Why is a primary database with noeviction safer than allkeys-lru?

  9. 9.How are LRU and LFU implemented in Redis, and when would you choose LFU?hard

    Neither is exact. Each object header has a 24-bit field. Under LRU it stores a coarse last-access clock. On eviction Redis samples maxmemory-samples keys, compares their idle time and keeps the best candidates in a 16-entry pool, which approximates true LRU closely at 5 to 10 samples without a linked list per key.

    Under LFU the same 24 bits hold an 8-bit logarithmic access counter plus a 16-bit minutes timestamp. The counter increments probabilistically (controlled by lfu-log-factor, default 10), so 255 can represent around a million hits, and it decays over time (decremented once per lfu-decay-time minutes, default 1) so yesterday's hot keys cool down.

    Choose LFU when popularity is skewed and stable: a nightly batch job that scans many keys once would flush hot keys out of an LRU cache, but barely moves LFU counters. Choose LRU when recency predicts reuse, as with sessions. OBJECT FREQ key shows the counter under LFU.

    What interviewers listen for
    • Both are approximated by sampling
    • LRU stores a coarse access clock
    • LFU stores a logarithmic counter that decays
    • LFU resists one-off scans polluting the cache

    Likely follow-up: What does raising maxmemory-samples cost? · How would you find hot keys under LFU?

  10. 10.Explain the cache-aside pattern with Redis. What are its strengths and weaknesses?easy

    In cache-aside (lazy loading), the application owns the logic:

    • Read: GET product:42. On a hit, return it. On a miss, load from the database, then SET product:42 <json> EX 300 and return.
    • Write: update the database, then delete the cache key so the next read reloads it.

    Strengths: only requested data is cached, the cache can fail without breaking reads (you fall back to the database), and it works with any data source.

    Weaknesses: the first read after a miss is slow, every miss costs three steps, and there is a window where cache and database disagree. Races exist: a reader can load an old row, a writer updates and deletes, then the reader writes the old value back. A TTL bounds how long that stale value can live. Popular keys expiring at once can also cause a stampede of database queries.

    It is the default pattern for read-heavy workloads.

    What interviewers listen for
    • Read: check cache, load on miss, populate with TTL
    • Write: update the database, then delete the key
    • Resilient to cache failures
    • TTL bounds staleness from races

    Likely follow-up: Why delete the key on write instead of updating it? · How would you prevent a stampede on a hot key?

  11. 11.Compare write-through and write-behind caching. When would you use each?mid

    Write-through: every write goes to the cache and the database synchronously, usually via a layer that writes both before acknowledging. The cache is always warm and consistent for keys written through it, but writes pay both latencies and you cache data that may never be read. If the second write fails, you need a compensation or retry policy.

    Write-behind (write-back): the application writes to Redis and returns; changes are flushed to the database asynchronously, often batched through a stream or queue. Writes are very fast and the database sees fewer, larger writes, which suits counters, likes and telemetry. The cost is durability: if Redis loses data before the flush (a crash with appendfsync everysec, a failover to a lagging replica), those writes are gone, and the database is eventually consistent.

    Read-through is the read-side counterpart where the cache layer loads misses itself. In practice, cache-aside plus delete-on-write covers most services; write-behind is reserved for high-volume data that tolerates loss or has a durable buffer.

    What interviewers listen for
    • Write-through: synchronous dual write, warm cache
    • Write-behind: async flush, fast writes, loss risk
    • Write-behind needs a durable buffer such as a stream
    • Cache-aside is the common default

    Likely follow-up: How would you make a write-behind pipeline survive a Redis restart?

  12. 12.How do you keep a Redis cache consistent with the database? Should you delete or update the key on write?mid

    Prefer update the database, then delete the key. Updating the cached value instead races: two writers can commit A then B to the database but write B then A to Redis, leaving A cached indefinitely. A delete is idempotent and lets the next reader load the committed value.

    Deleting first and then updating the database is worse: a reader can repopulate the old value between the two steps.

    Remaining gaps and fixes:

    • A slow reader can still write back a stale value after the delete. Short TTLs bound the damage; a delayed second delete shrinks the window further.
    • The delete can fail or the process can crash after the commit. Driving invalidation from the database's change stream (CDC, for example Debezium to a consumer that deletes keys) or an outbox makes it reliable.
    • Multiple services caching the same entity need a published invalidation event, not hidden deletes in one codebase.

    Also version or namespace keys (product:v3:42) when the cached shape changes, so a deploy cannot read incompatible data.

    What interviewers listen for
    • Update the database, then delete the cache key
    • Updating the cache on write races between writers
    • TTL as a safety net; delayed double delete
    • CDC or outbox for reliable invalidation

    Likely follow-up: Why is delete-then-update worse than update-then-delete? · How would you invalidate a cached list that contains the changed item?

  13. 13.What is a cache stampede (thundering herd), and how do you prevent it with Redis?hard

    When a hot key expires, every concurrent request misses at the same moment and all of them query the database and recompute the value. A query that normally runs once per TTL suddenly runs thousands of times, which can take the database down, and while it is slow even more requests pile up.

    Mitigations, often combined:

    • Request coalescing with a lock: the first miss acquires SET lock:product:42 <token> NX PX 5000 and rebuilds; others wait briefly and retry the cache, or serve a stale copy.
    • Stale-while-revalidate: store the value with a logical "fresh until" time and a longer real TTL. After the soft deadline, one request refreshes in the background while everyone gets the slightly stale value.
    • Probabilistic early expiration (XFetch): each reader refreshes early with a probability that rises as expiry approaches, so one request usually recomputes before the key expires.
    • TTL jitter: add randomness so keys loaded together do not expire together (a cache avalanche).
    • Warm-up of known hot keys after deploys or failovers.

    In-process single-flight also helps.

    What interviewers listen for
    • Many concurrent misses on one hot key overload the source
    • Lock so one request rebuilds
    • Serve stale while refreshing
    • Early probabilistic refresh and TTL jitter

    Likely follow-up: What happens if the lock holder crashes mid-rebuild? · How is a stampede different from cache penetration?

  14. 14.Explain cache penetration, cache breakdown and cache avalanche, and a fix for each.mid

    These terms come up often in system design rounds:

    • Cache penetration: requests for keys that do not exist in the database (random IDs, a scraper, an attack). Every one misses the cache and hits the database. Fix: cache the negative result briefly (SET product:999 "__none__" EX 60) and reject impossible IDs early, for example with a Bloom filter of valid IDs (BF.ADD / BF.EXISTS, built into Redis 8).
    • Cache breakdown: a single hot key expires and a burst of requests rebuilds it at once. This is the stampede case. Fix: a rebuild lock, stale-while-revalidate, or no hard expiry for that key with background refresh.
    • Cache avalanche: many keys expire at the same time (all loaded at startup with the same TTL), or the cache cluster goes down. Fix: add TTL jitter, keep Redis highly available (replicas, Sentinel or Cluster), and protect the database with rate limits, circuit breakers and request timeouts.

    The common thread is protecting the backing store from load the cache normally absorbs.

    What interviewers listen for
    • Penetration: missing keys; cache nulls, Bloom filter
    • Breakdown: one hot key expires; lock or stale serve
    • Avalanche: mass expiry or outage; jitter and HA
    • Protect the database with limits and breakers

    Likely follow-up: What TTL would you give a negative cache entry, and why short?

  15. 15.Compare RDB and AOF persistence. Which would you enable in production?mid

    RDB writes point-in-time snapshots: BGSAVE forks a child that writes a compact binary file while the parent keeps serving. Files are small, restart is fast, and they make good backups, but you lose everything since the last snapshot (minutes, with the default save rules).

    AOF logs every write command. appendfsync controls durability:

    • always: fsync on every write batch; safest, slowest.
    • everysec (default): fsync once a second in a background thread; lose about a second of writes.
    • no: let the OS flush, typically within 30 seconds.

    The AOF grows, so Redis rewrites it in the background. Since Redis 7 it is a multi-part AOF: a base file (RDB format by default) plus incremental files tracked by a manifest.

    For data you care about, enable AOF with everysec (which uses the RDB preamble) and keep RDB snapshots for backups. For a pure cache, persistence can be off or RDB-only; remember that a replica restarting with no data is still safe because it resyncs from the primary.

    What interviewers listen for
    • RDB: forked snapshots, compact, lose minutes
    • AOF: command log; fsync always, everysec or no
    • everysec loses about one second
    • Redis 7+ multi-part AOF with RDB base

    Likely follow-up: Why is it dangerous to run a primary with persistence off and auto-restart? · What does BGREWRITEAOF do?

  16. 16.Your 40 GB Redis instance shows latency spikes and memory jumps whenever a snapshot runs. Why?hard

    BGSAVE, AOF rewrites and full replica syncs all call fork(). Two costs follow.

    Fork latency: the kernel copies the parent's page tables, proportional to memory size. On a 40 GB process that can stall the main thread for a noticeable time, worse on some virtualized hosts. INFO stats exposes latest_fork_usec.

    Copy-on-write memory: parent and child share pages until the parent writes. A write-heavy workload touches many pages during the snapshot, and each touched page is duplicated, so memory can grow toward double in the worst case. If the box has no headroom, the OOM killer may kill Redis. Transparent huge pages make it worse (a one-byte write copies 2 MB), which is why Redis warns at startup when THP is enabled.

    Fixes: disable THP, size maxmemory well below RAM, set vm.overcommit_memory = 1 so fork does not fail, run persistence on replicas instead of the primary, shard into smaller instances, and schedule snapshots off-peak.

    What interviewers listen for
    • fork copies page tables: latency grows with memory
    • Copy-on-write duplicates pages written during the save
    • Disable transparent huge pages
    • Headroom, overcommit_memory=1, smaller shards

    Likely follow-up: How do you measure fork time? · Why move snapshots to a replica?

  17. 17.How does Redis replication work, and can a client lose an acknowledged write?mid

    A replica connects to the primary (REPLICAOF host port) and asks to continue from its replication ID and offset with PSYNC. If the primary's in-memory backlog (repl-backlog-size, 1 MB by default) still holds the missing range, it sends only that (partial resync). Otherwise it does a full resync: an RDB snapshot (streamed diskless by default since Redis 7) followed by the buffered write stream.

    Replication is asynchronous: the primary acknowledges the client before replicas receive the write. So yes, if the primary dies right after acknowledging and a replica is promoted, that write is lost.

    Mitigations reduce the window but do not make Redis strongly consistent:

    • WAIT numreplicas timeout blocks until replicas have the write; WAITAOF (7.2) also waits for fsync.
    • min-replicas-to-write and min-replicas-max-lag make an isolated primary stop accepting writes.

    Replicas are read-only by default, and reads from them may be slightly stale.

    What interviewers listen for
    • PSYNC with replication ID and offset
    • Partial resync from the backlog, else full resync
    • Asynchronous: acknowledged writes can be lost on failover
    • WAIT and min-replicas narrow the window

    Likely follow-up: Why should you increase repl-backlog-size on a busy primary? · Does WAIT make Redis linearizable?

  18. 18.What does Redis Sentinel do, and how does a failover happen?mid

    Sentinel provides high availability for a non-clustered primary with replicas. Sentinel processes (at least three, on separate failure domains) monitor the primary and replicas, act as a configuration provider for clients, and run failovers.

    The failover sequence:

    • A Sentinel that gets no valid reply for down-after-milliseconds marks the primary subjectively down (SDOWN).
    • When at least quorum Sentinels agree, it becomes objectively down (ODOWN).
    • A Sentinel leader is elected, which requires votes from a majority of all Sentinels, not just the quorum.
    • The leader promotes the best replica (priority, replication offset, run ID), reconfigures the others to follow it, and publishes the new address.

    Clients do not hard-code the primary. They ask Sentinel (SENTINEL get-master-addr-by-name mymaster) and reconnect on the switch event. Because replication is asynchronous, writes acknowledged by the old primary but not replicated are lost, and a partitioned old primary can keep accepting writes unless min-replicas-to-write is set.

    What interviewers listen for
    • HA for primary plus replicas, without sharding
    • SDOWN, then ODOWN once quorum agrees
    • Majority needed to elect a leader and fail over
    • Clients discover the primary through Sentinel

    Likely follow-up: Why do you need at least three Sentinels? · When do you choose Cluster over Sentinel?

  19. 19.How does Redis Cluster distribute keys across nodes?mid

    The keyspace is split into 16,384 hash slots. A key's slot is CRC16(key) mod 16384, and each primary owns a subset of slots (each with replicas). Clients cache the slot-to-node map (CLUSTER SHARDS) and send each command straight to the owner, so there is no proxy hop.

    If the client is wrong, the node answers with a redirect instead of forwarding:

    • MOVED 3999 10.0.0.3:6379: the slot lives elsewhere permanently; update the map.
    • ASK: the slot is being migrated; retry this one command on the target after sending ASKING.

    Resharding moves slots key by key (MIGRATE) while the cluster stays online. Nodes gossip over a separate cluster bus port (data port + 10000), detect failures, and promote a replica when a majority of primaries agree.

    Limits: only database 0, multi-key commands need all keys in one slot, and like Sentinel setups it uses asynchronous replication, so acknowledged writes can be lost on failover.

    What interviewers listen for
    • 16,384 slots, CRC16(key) mod 16384
    • Smart clients route directly using a slot map
    • MOVED is permanent, ASK is mid-migration
    • Single database, same-slot multi-key operations

    Likely follow-up: Why 16,384 slots and not consistent hashing with a ring? · What happens to clients during resharding?

  20. 20.What is the difference between MOVED and ASK redirects in Redis Cluster?hard

    Both tell the client "this node does not serve that key", but they mean different things.

    MOVED means the slot is now owned by another node. The client should retry there and update its slot map, because every future command for that slot belongs to the new owner. Smart clients usually refresh the whole map after a MOVED, since one move often means many.

    ASK happens only during a slot migration. The source node still owns the slot but no longer has this particular key, which already moved. The client must send ASKING followed by the command to the target node, for this one request only, and must not update its map. Without ASKING, the target would reply MOVED back to the source, because it does not own the slot yet.

    When migration finishes, the slot ownership flips and clients start receiving MOVED. Multi-key commands during migration can fail with TRYAGAIN if their keys are split between source and target.

    What interviewers listen for
    • MOVED: permanent, update the slot map
    • ASK: temporary, one request, during migration
    • ASKING must precede the retried command
    • TRYAGAIN for split multi-key operations

    Likely follow-up: What does a client library do if it receives MOVED for every request after a failover?

  21. 21.Your code runs MSET user:1:name Ana user:2:name Bo against Redis Cluster and gets CROSSSLOT. Why, and how do you fix it?mid

    In Cluster, commands with several keys (MSET, MGET, SUNION, RENAME), transactions and Lua scripts only work when every key hashes to the same slot, because a node can only atomically operate on data it owns. user:1:name and user:2:name hash to different slots, so the node rejects the command with CROSSSLOT.

    Fixes:

    • Hash tags: if a key contains {...}, only the substring inside the first braces is hashed. {user:1}:name and {user:1}:email always share a slot, so one user's keys can be updated together in MULTI or a script.
    • Split the command: many clients turn MGET into per-slot calls and merge the results. You lose atomicity across keys, but often you never needed it.

    Do not put everything under one tag such as {app}: all those keys land on one shard, creating a hot slot that cannot be scaled out. Tag by the entity that needs atomic updates.

    What interviewers listen for
    • Multi-key operations need one slot
    • Hash tags hash only the text inside braces
    • Tag by entity, never one global tag
    • Clients can split commands per slot without atomicity

    Likely follow-up: How would you design keys for a per-user rate limiter that also touches a global counter?

  22. 22.How do you implement a distributed lock with a single Redis instance? What are the pitfalls?mid

    Acquire with one atomic command: SET lock:order:42 <random-token> NX PX 30000. NX only sets the key if it is absent, PX gives it an expiry so a crashed holder cannot block others forever, and the random token identifies the owner. A reply of OK means you hold the lock; nil means someone else does.

    Release with a compare-and-delete Lua script: delete only if the value still equals your token. A plain DEL is a bug: if your work outlived the TTL, the lock expired, another client acquired it, and your DEL removes their lock.

    Pitfalls:

    • SETNX followed by EXPIRE is two commands; a crash between them leaves a lock with no expiry.
    • Pick a TTL longer than the work, or extend it with a watchdog that renews only while you still own it.
    • A GC pause or network delay can outlast the TTL, so two clients may believe they hold the lock. For correctness, pass a fencing token to the protected resource and reject stale ones.
    • Async replication: a failover can lose the lock key.
    What interviewers listen for
    • SET key token NX PX in one command
    • Release with a Lua compare-and-delete
    • TTL plus renewal; never SETNX then EXPIRE
    • Fencing tokens for correctness

    Likely follow-up: What is a fencing token and who checks it? · How does Redisson implement lock renewal?

  23. 23.What is Redlock, and what is the critique of it?hard

    Redlock is the algorithm described in the Redis docs for locks that survive single-node failure. Use N independent primaries (typically five). The client tries SET key token NX PX ttl on each with a short timeout, and holds the lock only if it acquired a majority and the elapsed time is less than the TTL. The remaining validity is TTL minus elapsed time minus a clock drift allowance. On failure it releases everywhere.

    Martin Kleppmann's 2016 critique: a lock with an expiry cannot guarantee mutual exclusion when a client can pause (GC, page faults, network delay) past the TTL and then act as if it still holds the lock. Redlock also assumes bounded clock drift and network delay, and a node that restarts without persisted state can grant the lock again. His conclusion: for efficiency (avoiding duplicate work) a single Redis lock is enough; for correctness use fencing tokens with a consensus-based system such as ZooKeeper or etcd. Antirez replied defending Redlock's assumptions. A strong answer names both sides and asks which kind of lock is needed.

    What interviewers listen for
    • Majority of N independent primaries within the TTL
    • Validity time accounts for elapsed time and drift
    • Critique: process pauses and timing assumptions
    • Efficiency locks vs correctness locks with fencing

    Likely follow-up: Why does Redlock not provide fencing tokens? · When would you use a database row lock instead?

  24. 24.Design an API rate limiter with Redis. Compare fixed window, sliding window and token bucket.mid

    Fixed window: key per client per window (rl:42:1700000040), INCR it and set an expiry when the count is 1. Cheap, one key, but a burst at the window boundary can let through twice the limit in a short span.

    Sliding window log: a sorted set of request timestamps. Remove entries older than the window (ZREMRANGEBYSCORE), count (ZCARD), add if under the limit. Exact, but stores one member per request. The sliding window counter approximates it with two fixed-window counters weighted by overlap.

    Token bucket: a hash with tokens and last_refill. Each request refills tokens by elapsed time times rate, capped at capacity, then spends one. It allows controlled bursts and a smooth average rate, in constant memory.

    Whatever the algorithm, the read-check-write must be atomic, so implement it as a Lua script (or a function). Use redis.call('TIME') or a single clock source to avoid skew between app servers, return the remaining quota for headers, and decide whether to fail open or closed when Redis is unavailable.

    What interviewers listen for
    • Fixed window: INCR plus expiry; boundary bursts
    • Sliding log: sorted set, exact, more memory
    • Token bucket: bursts with steady average rate
    • Atomic via Lua; clock source; fail-open policy

    Likely follow-up: How would you rate limit across a Redis Cluster? · Why is INCR then EXPIRE in two calls risky?

  25. 25.How do Redis transactions (MULTI/EXEC) work? Do they roll back on error?mid

    After MULTI, commands are queued (each replies QUEUED), and EXEC runs them all in sequence with no other client's command interleaved. DISCARD drops the queue.

    There is no rollback:

    • If a command fails at queue time (unknown command, wrong number of arguments), EXEC refuses with EXECABORT and nothing runs.
    • If a command fails at execution time (for example LPUSH on a string gives WRONGTYPE), that command returns an error and the others still apply.

    Inside MULTI you cannot read a value and branch on it, because replies arrive only at EXEC. For read-modify-write, use WATCH: watch the keys, read them, then MULTI ... EXEC. If any watched key changed in between, EXEC returns a null reply and you retry. That is optimistic locking.

    Under contention, WATCH retries can spin, so a Lua script is usually simpler: it reads, decides and writes atomically on the server in one round trip.

    What interviewers listen for
    • Commands queue, then run without interleaving at EXEC
    • Queue-time errors abort; runtime errors do not roll back
    • WATCH gives optimistic check-and-set
    • Lua is simpler for conditional logic

    Likely follow-up: What does EXEC return when a watched key was modified? · Are transactions durable if the server crashes during EXEC?

  26. 26.Why use Lua scripts in Redis, and what rules should a script follow?mid

    A script sent with EVAL runs atomically on the server: no other command runs until it finishes. That makes conditional logic (check then write) safe without WATCH loops, and saves round trips.

    Rules and practices:

    • Declare every key in KEYS, and pass other values in ARGV. Redis Cluster routes the script by its keys, and all keys must be in one slot.
    • Keep scripts short. A long script blocks every client; after busy-reply-threshold (5 seconds by default) others get BUSY, and SCRIPT KILL only works if the script has not written yet.
    • Load once and call by hash with EVALSHA; handle NOSCRIPT by falling back to EVAL (client libraries do this).
    • Know the type conversions: a Lua number returns as an integer (3.99 becomes 3), false becomes nil, and a table stops at the first nil.
    • redis.call raises on error, redis.pcall returns the error to handle.

    Since Redis 7, Functions (FUNCTION LOAD, FCALL) are the managed alternative: named, persisted and replicated with the dataset.

    What interviewers listen for
    • Scripts run atomically and save round trips
    • Pass keys in KEYS for Cluster routing
    • Long scripts block everyone (BUSY)
    • EVALSHA caching; Functions in Redis 7+

    Likely follow-up: What happens to a script mid-way when the server crashes? · Why must scripts be deterministic in older Redis versions?

  27. 27.What is pipelining in Redis, and how is it different from a transaction?easy

    Without pipelining, each command waits a full network round trip for its reply. With 1 ms RTT, 10,000 GETs take at least 10 seconds even though Redis processes each in microseconds. Pipelining sends many commands without waiting, then reads all replies. It also cuts syscalls, because many commands are read and written per socket call.

    It is purely a client-side and network optimization:

    • Not atomic: other clients' commands can run between yours.
    • Each command still succeeds or fails on its own.
    • The server buffers the replies in memory, so very large pipelines should be split into batches (for example 1,000 commands).

    A transaction (MULTI/EXEC) guarantees that the queued commands run without interleaving, but by itself still pays a round trip per command unless you also pipeline it, which most clients do. Lua scripts give atomicity and conditional logic in one round trip.

    Pipelining is the first fix when a batch job is slow because of many small calls.

    What interviewers listen for
    • Removes per-command round-trip waits
    • Not atomic: commands can interleave
    • Batch large pipelines to bound buffers
    • MULTI adds isolation; Lua adds logic

    Likely follow-up: How does pipelining interact with Redis Cluster?

  28. 28.When would you use Redis Pub/Sub, and when Redis Streams?mid

    Pub/Sub (PUBLISH, SUBSCRIBE) is fire-and-forget fan-out. Messages are delivered to whoever is subscribed at that instant and are not stored. A subscriber that is disconnected, restarting or too slow misses messages, and a slow subscriber is cut off when its output buffer exceeds client-output-buffer-limit pubsub. Good for ephemeral notifications: cache invalidation broadcasts, live dashboards, chat presence. Redis 7 added sharded Pub/Sub (SPUBLISH) so messages stay within one shard in Cluster.

    Streams (XADD, XREADGROUP) are an append-only log stored in Redis. Messages persist (with MAXLEN or MINID trimming), readers can resume from an ID, and consumer groups split work across consumers with acknowledgements: each delivered entry stays in the pending entries list until XACK, and XAUTOCLAIM reassigns entries from dead consumers. That gives at-least-once processing.

    Choose Pub/Sub when losing a message is acceptable; Streams when every event must be processed. For very high volume or long retention, Kafka is the better tool.

    What interviewers listen for
    • Pub/Sub: no storage, at-most-once, live subscribers only
    • Streams: persisted log with IDs and trimming
    • Consumer groups, pending list, XACK, XAUTOCLAIM
    • Streams give at-least-once processing

    Likely follow-up: How do you handle a stream entry that fails processing repeatedly? · How is a Redis Stream different from a Kafka topic?

  29. 29.What are big keys and hot keys? How do you find and fix them?hard

    A big key holds a huge value or collection: a 50 MB string, a hash with millions of fields. Commands like HGETALL, SMEMBERS or DEL on it take a long time on the single execution thread and block every client; it also skews memory across Cluster shards and slows migrations. A hot key receives a disproportionate share of traffic, saturating the one shard (and one CPU) that owns it.

    Finding them:

    • redis-cli --bigkeys and --memkeys scan the keyspace with SCAN; MEMORY USAGE key checks one key.
    • redis-cli --hotkeys works under an LFU policy; SLOWLOG GET and LATENCY DOCTOR show the damage.
    • Never use KEYS * to look: it is O(N) and blocks.

    Fixes: split big collections into buckets (followers:42:{0..63}), iterate with HSCAN/SSCAN, delete with UNLINK (frees memory in a background thread). For hot keys, add a short-lived local in-process cache, read from replicas, or replicate the key under several names (config:v1#0..7) and pick one at random.

    What interviewers listen for
    • Big keys block the main thread and skew shards
    • Hot keys overload one shard
    • --bigkeys, --memkeys, --hotkeys, MEMORY USAGE, SLOWLOG
    • Split, SCAN-iterate, UNLINK; local cache or key copies

    Likely follow-up: Why does SCAN sometimes return the same key twice? · How would you delete a hash with 20 million fields safely?

  30. 30.Redis memory keeps growing. How do you investigate and reduce it?hard

    Start with INFO memory: used_memory (what Redis allocated), used_memory_rss (what the OS sees), and mem_fragmentation_ratio. A ratio well above 1.5 suggests fragmentation; below 1 means swapping. MEMORY DOCTOR, MEMORY STATS and redis-cli --memkeys show where memory goes.

    Common wins:

    • Use compact encodings. Small hashes, sets and sorted sets are stored as a listpack (or intset for small integer sets) until they exceed thresholds such as hash-max-listpack-entries 128 and hash-max-listpack-value 64. Grouping many tiny string keys into hashes of about 100 fields can save a lot of per-key overhead. Check with OBJECT ENCODING.
    • Set TTLs on everything that is cache, and check that keys without TTLs are intended.
    • Shorter keys and values: compress large values client-side; avoid storing full JSON when a few fields are read.
    • Fragmentation: activedefrag yes (jemalloc) reclaims memory online.
    • Leave headroom for client output buffers, replication backlog and fork copy-on-write.
    What interviewers listen for
    • INFO memory: used vs RSS and fragmentation ratio
    • Listpack and intset encodings for small collections
    • TTLs and key or value size discipline
    • activedefrag for fragmentation

    Likely follow-up: Why can RSS stay high after you delete half the keys? · What does OBJECT ENCODING return for a hash with 200 fields?

Prefer multiple choice? All 20 Redis MCQs with answers →

esc