Ideal answer Redis executes commands in a mostly single-threaded event loop, which avoids thread contention and context switching. It’s fast because:
- Data is in-memory (no disk seek per operation).
- Commands are O(1) or near-O(1) for common structures.
- I/O is handled via non-blocking sockets + multiplexing (epoll/kqueue), so many connections can be served efficiently.
Senior notes
- “Single-threaded” is primarily the command execution path; some tasks can be multi-threaded in modern Redis (e.g., certain background I/O/persistence), but command semantics remain sequential.
Follow-ups
- Redis slows when commands block (big keys, slow Lua, persistence fork latency, network saturation).
Ideal answer
- RDB (snapshot): periodically writes a point-in-time dump. Pros: compact, fast restart. Cons: can lose recent writes (between snapshots), fork can cause latency spikes.
- AOF (append-only file): logs every write. Pros: better durability; with
appendfsync everysec, typical loss window is ~1s. Cons: larger files, slower restart (replay), rewrite overhead.
Production choice
- If I need stronger durability: AOF everysec (often with AOF rewrite).
- If I mostly want fast restart and can tolerate some loss: RDB.
- Common: AOF enabled + periodic RDB / hybrid, depending on ops posture and acceptable loss.
Ideal answer It depends on persistence mode:
- With AOF, on restart Redis replays the AOF. If the last command is partially written/corrupted, Redis can truncate/repair (depending on configuration/tools) and load up to the last valid entry—so you may lose the tail.
- With RDB, you restore to the last snapshot → data after snapshot is lost.
With appendfsync everysec, you may lose up to ~1 second of writes on crash (OS buffers).
Ideal answer Only when the data model and durability requirements fit:
- Ephemeral / TTL-based data: sessions, OTP, rate limits.
- Derived / cacheable data where DB is source of truth.
- Ultra-low latency counters/leaderboards where occasional loss is acceptable or mitigated (streaming rebuild).
If the system needs strong durability, complex queries, and strict consistency, Redis should not be the primary store; it should be a cache/accelerator.
Ideal answer
- Hash: optimized for small hashes (compact encoding) and switches to hashtable when it grows. Great for grouping fields by key (user profile, settings).
- Sorted Set (ZSET): typically implemented with a skiplist + hash map for member→score lookup. Great for leaderboards, ranking, time-based ordering.
- List: implemented as quicklist-like structure (linked list of compact blocks) to balance memory and speed. Good for queues, feeds (though Streams often better now).
Senior note Internal encodings change across versions; what matters in interviews is: Redis uses space-efficient encodings for small values, and upgrades to more general structures as it grows.
Ideal answer Redis uses an allocator (commonly jemalloc) and supports:
maxmemoryto cap memory usage- eviction policies:
noeviction(writes fail)allkeys-lru/lfu,volatile-lru/lfu(only keys with TTL),random,ttlWhen memory is full:
- With eviction policy, Redis evicts keys according to the policy.
- With
noeviction, write commands that increase memory fail.
Senior angle Memory fragmentation can be a real issue; monitoring mem_fragmentation_ratio, big keys, and allocator behavior matters.
Ideal answer Redis Cluster partitions the keyspace into hash slots (fixed number: 16384) using a CRC16 hash → slot mapping. Each slot is assigned to a master node; replicas follow that master. Why fixed slots:
- Simplifies rebalancing: move slots between nodes instead of rehashing everything.
- Clients can route requests based on slot metadata and handle MOVED/ASK redirections.
Ideal answer Two common HA setups:
- Redis Sentinel (non-cluster): Sentinels detect failure, elect a leader, promote a replica, and reconfigure clients.
- Redis Cluster: cluster nodes detect failure, and if quorum is satisfied, a replica is promoted to master and takes over the slots.
Senior pitfalls
- Failover can cause brief unavailability and potential data loss if replication lag exists.
- Split-brain can happen if quorum/partition issues; correctness depends on topology and settings.
Ideal answer In Cluster, resharding is slot migration:
- Pick slots to move from source master → target master.
- During migration, clients may get
ASKredirections; after migration,MOVED. Impact: - Increased latency during migration due to extra hops/redirects.
- Operational complexity: careful planning to avoid hot-slot imbalance.
Ideal answer Use a single atomic command:
- Acquire:
SET lock_key unique_value NX PX ttlNXensures only one ownerPX ttlensures auto-expire
Release safely:
- Only the owner should unlock → compare the stored value with your
unique_value - Use Lua to check-and-del atomically:
- If value matches → DEL
- Else do nothing
Senior pitfalls
- TTL too short → lock expires while work still running → two owners
- TTL too long → slow recovery if owner dies
- Always pick unique values (UUID) per lock holder.
Ideal answer Redlock is an algorithm for locks across multiple independent Redis masters (acquire lock in a majority within a time window). Senior stance:
- It improves safety under some failure modes, but network partitions, clock drift, and GC pauses can still break assumptions.
- For money-critical or strict correctness (e.g., banking ledger), I’d prefer DB constraints/transactions or a consensus system (ZooKeeper/etcd) rather than Redis locking.
Ideal answer Yes, but lock ≠ idempotency key.
- Idempotency is about “same request → same effect/result.”
- A lock only serializes execution; retries after lock expiry can still duplicate effects.
Better pattern:
- Store idempotency key with result (or status) in Redis/DB.
- Use
SETNX/SET NX PXto “claim” processing. - Persist final outcome so retries can return the same response without reprocessing.
Ideal answer Redis has MULTI/EXEC:
- Commands queued then executed sequentially.
- It is atomic (no interleaving during EXEC), but not ACID:
- No rollback if a command fails at runtime.
- No isolation like DB levels beyond single-thread sequencing. For conditional updates, use Lua scripts or WATCH (optimistic concurrency).
Ideal answer Lua scripts provide:
- Atomic multi-step operations (check then set, conditional increments, etc.)
- Fewer round trips → lower latency Use cases:
- Safe unlock
- Rate limiter token bucket
- Compare-and-swap updates
Risk Lua runs in the main thread; long scripts block Redis → must keep scripts short and predictable.
Ideal answer
- Cache-aside: app reads cache; on miss reads DB then populates cache. Writes go to DB then invalidate/update cache.
- Pros: simple, flexible
- Cons: stale data risk, stampede on hot miss
- Write-through: app writes cache, cache writes DB synchronously.
- Pros: cache always warm/consistent
- Cons: added write latency, cache becomes critical path
- Write-behind: app writes cache; cache flushes to DB asynchronously.
- Pros: very fast writes
- Cons: risk of data loss, complex recovery
Ideal answer Use one or combine:
- Mutex / lock on miss: only one request rebuilds, others wait/fallback.
- Request coalescing: collapse concurrent rebuilds.
- Early refresh / soft TTL: refresh before expiration in background.
- Jittered TTL: avoid synchronized expirations.
Ideal answer Cache penetration = many requests for non-existing keys hitting DB. Mitigations:
- Cache nulls (short TTL) for misses
- Bloom filter in front
- Rate limit / WAF for abusive patterns
Ideal answer Because bottlenecks may be:
- Network saturation (NIC/packet processing)
- Blocking commands on big keys
- Fork/persistence causing latency spikes (copy-on-write overhead)
- Disk I/O during AOF rewrite
- Slow clients / output buffer issues
Ideal answer A big key is a key with very large value or huge collection (big hash/zset/list). Problems:
- Single command takes long time, blocks event loop
- Replication and persistence become heavy
- Memory fragmentation & eviction inefficiency
Detection:
SCAN+MEMORY USAGE <key>- Monitor latency/slowlog Fix:
- Split into multiple keys (sharding by userId:part)
- Use better structures (Streams instead of huge Lists)
- Limit per-key size and enforce via app logic
Ideal answer
- Never expose Redis publicly
- Use private network/VPC, security groups/firewall
- Enable AUTH/ACL (least privilege)
- Enable TLS if crossing untrusted networks
- Disable dangerous commands in shared environments (or restrict via ACL)
- Monitor config drift
Ideal answer Key signals:
- Latency percentiles (p95/p99) and slowlog
- Memory usage, fragmentation ratio
- Evictions and keyspace hits/misses
- Replication lag, number of connected clients
- AOF rewrite status / fork time spikes
Alerting:
- sustained high latency
- evictions increasing unexpectedly
- replication lag growing
- near maxmemory + rising fragmentation
Ideal answer Cache can keep reads alive temporarily if data is already cached. But:
- Cache-aside misses will fail (can’t load from DB). Mitigations:
- Serve stale cache (stale-while-revalidate)
- Circuit breaker: stop DB calls, degrade gracefully
- Fallback default responses for non-critical endpoints
- Pre-warm critical keys / protect hot paths
Ideal answer I’d check in order:
- Redis metrics:
INFO, latency stats, slowlog - Persistenc