Everything you need to ace your Redis interview, from fundamentals to advanced topics.
Introduction
Preparing for a Redis interview? Whether you're a senior engineer brushing up on fundamentals or someone new to Redis, this guide covers everything you need to know. I've compiled questions, answers, and practical examples that I've used in real interviews and while building production systems.
This guide is structured in three levels: Fundamentals (must know), Intermediate (production ready), and Advanced (expert level). Each section builds on the previous one, so you can follow along at your own pace.
LEVEL 1: FUNDAMENTALS (Must Know)
What is Redis?
Redis stands for Remote Dictionary Server. It's an in-memory data structure store that's incredibly fast:
- Key-Value store: Data stored as key-value pairs (dictionary-like structure)
- Multiple data types: Strings, Lists, Sets, Hashes, Sorted Sets (different types supported)
- Persistence options: Can save to disk (memory + disk options)
- Single-threaded: One command at a time (no race conditions)
Key Features:
- Speed: Microsecond latency (extremely fast - stored in memory)
- Atomic operations: Operations are all-or-nothing (no partial failures)
- Pub/Sub: Message broadcasting (publish events and subscribe to them)
- Transactions: Multiple commands as one unit (execute multiple commands together)
- Replication: Master-slave setup (backup copies for high availability)
Common Use Cases:
- Caching: Database query results, API responses (cache repeated data)
- Session storage: User sessions (store login info for fast access)
- Real-time analytics: Counters, leaderboards (live counts, rankings)
- Message queues: Task queues, job processing (background jobs)
- Rate limiting: API throttling (limit requests)
Interview Questions - Level 1
Q: What is Redis and why would you use it?
A: Redis is an in-memory data structure store (faster than milliseconds):
- Primary use: Caching layer between application and database (reduce DB load)
- Speed: 100K+ ops/sec possible (extremely fast - stored in memory)
- Data structures: Not just strings - lists, sets, hashes (supports complex data types)
- Persistence: Optional disk storage (memory + disk combination)
When to use Redis:
- Frequently accessed data (cache repeated queries)
- Session management (user login data)
- Real-time features (leaderboards, counters, notifications)
- Pub/Sub messaging (real-time events)
- Rate limiting (limit API calls)
Q: What are Redis data types?
A: Redis supports multiple data structures:
- String: Simple key-value (basic type - text, numbers, binary)
SET user:1:name "John"
GET user:1:name
INCR view_count
- List: Ordered collection (array-like - push/pop from both ends)
LPUSH tasks "task1"
RPUSH tasks "task2"
LPOP tasks
- Set: Unique unordered collection (no duplicates - unique values)
SADD tags "python" "redis"
SMEMBERS tags
SISMEMBER tags "python"
- Hash: Key-value pairs within a key (object-like - nested key-values)
HSET user:1 name "John" age 30
HGET user:1 name
HGETALL user:1
- Sorted Set: Set with scores (maintains ranking - perfect for leaderboards)
ZADD leaderboard 100 "player1"
ZADD leaderboard 200 "player2"
ZRANGE leaderboard 0 -1 WITHSCORES
Q: How does Redis achieve high performance?
A: Multiple factors contribute:
- In-memory storage: No disk I/O for reads (direct from RAM - microseconds)
- Single-threaded: No context switching, no locks (simple execution model - automatic thread safety)
- Optimized data structures: Efficient implementations (memory-optimized structures)
- Event-driven: Non-blocking I/O (async operations - one thread handles many requests)
- Simple protocol: Low parsing overhead (RESP protocol - simple text-based)
Q: What is the difference between Redis and Memcached?
A:
Redis:
- Multiple data types (strings, lists, sets, hashes, sorted sets)
- Persistence options (RDB snapshots, AOF logs)
- Replication and clustering (HA support - high availability)
- Pub/Sub messaging (real-time events)
- Transactions and Lua scripts (complex operations)
- Single-threaded (predictable performance)
Memcached:
- Only strings (simple key-value)
- No persistence (memory only - data lost on restart)
- Multi-threaded (better CPU utilization)
- Simpler, lighter (less features, less overhead)
- Better for simple caching (pure cache use case)
Use Redis when: Need data structures, persistence, pub/sub
Use Memcached when: Simple string caching, multi-core utilization
Q: Explain Redis persistence - RDB vs AOF.
A: Redis has two persistence options:
RDB (Redis Database):
- Point-in-time snapshots (full backup at specific time)
- Compact binary format (compressed - storage efficient)
- Faster restarts (loads single file)
- Periodic saves:
SAVE 900 1(save if 1 change in 900 seconds) - Con: Can lose data between snapshots (data after last snapshot is lost)
AOF (Append-Only File):
- Logs every write operation (records every command - like Git log)
- Better durability (minimum data loss - data up to latest)
- Rewritable (can compact - remove duplicates)
-
appendfsyncoptions:-
always: Every command (slow but safe) -
everysec: Every second (balanced - max 1 sec loss) -
no: OS decides (fast but risky)
-
- Con: Larger files, slower restarts
Best practice: Use both (RDB for backups, AOF for durability)
Q: What is Redis eviction policy?
A: When memory is full, Redis evicts keys based on policy (deletes keys when memory is full):
Eviction policies:
-
noeviction: Return errors (doesn't delete - returns error) -
allkeys-lru: Remove least recently used (delete old data from all keys - most common) -
allkeys-lfu: Remove least frequently used (delete less frequently used data) -
volatile-lru: LRU among keys with TTL (from keys with expire set) -
volatile-lfu: LFU among keys with TTL -
volatile-random: Random among keys with TTL -
volatile-ttl: Remove keys with shortest TTL (keys expiring soon first) -
allkeys-random: Random removal
Common choice: allkeys-lru for cache (automatically clears least used data)
Q: How do you set expiration on keys?
A: Multiple commands for TTL (time to live):
SET key value EX 300 # Expire in 300 seconds
SET key value PX 300000 # Expire in 300000 milliseconds
SETEX key 300 value # Set with expiration
EXPIRE key 300 # Set expiration on existing key
TTL key # Check remaining time
PERSIST key # Remove expiration
Use cases:
- Session data (auto logout after inactivity)
- Cache entries (auto refresh stale data)
- Rate limiting (reset counters periodically)
- OTP codes (expire after time)
LEVEL 2: INTERMEDIATE (Production Ready)
Transactions:
- MULTI/EXEC: Group commands atomically (execute multiple commands together - all or nothing)
- WATCH: Optimistic locking (check then update - avoid race condition)
- DISCARD: Cancel transaction (like rollback)
Pub/Sub:
- PUBLISH/SUBSCRIBE: Message broadcasting (publish events and subscribe)
- PSUBSCRIBE: Pattern-based subscriptions (wildcard patterns)
- Channels: Topic-based messaging (like topics - specific channels)
Pipelining:
- Batch commands: Reduce network round-trips (send multiple commands together)
- Performance: Massive throughput increase (reduces RTT overhead)
Lua Scripts:
- EVAL: Execute Lua code in Redis (server-side logic)
- Atomic execution: Script runs atomically (no interruption in middle)
- EVALSHA: Cached scripts (call using script hash - efficient)
Replication:
- Master-Slave: Read replicas (write to master, read from slaves)
- Asynchronous: Eventual consistency (not immediate sync - slight delay)
- Replication lag: Slaves may be behind (monitor this)
- Read scaling: Distribute reads across slaves
Redis Sentinel:
- High availability: Automatic failover (detects master failure)
- Monitoring: Watches master and slaves (continuous monitoring)
- Automatic promotion: Promotes slave to master (when master fails)
- Quorum: Requires multiple sentinels (consensus needed)
Redis Cluster:
- Sharding: Data distributed across nodes (horizontal scaling)
- Hash slots: 16384 slots distributed (keys mapped to slots)
- Automatic failover: Node failure handling (self-healing)
- No single point of failure: Distributed architecture
LEVEL 3: ADVANCED (Expert Level)
Q: How do you optimize Redis memory usage?
A: Multiple strategies:
1. Use appropriate data types:
# BAD - Using string for list
SET users "user1,user2,user3"
# GOOD - Using list
LPUSH users "user1" "user2" "user3"
2. Enable compression:
- Use ziplist encoding (small lists/hashes stored efficiently - memory compact)
3. Set TTL on everything:
SETEX key 3600 value # Auto cleanup
4. Use compression:
- Compress values before storing (client-side compression)
- Trade CPU for memory (need to decompress when using)
5. Avoid large keys:
- Split large values into smaller chunks (like pagination)
- Use sorted sets instead of large lists
6. Monitor memory:
MEMORY USAGE key # Memory used by key
MEMORY STATS # Memory statistics
Q: How do you implement rate limiting with Redis?
A: Multiple approaches:
1. Fixed Window:
def is_allowed(user_id, limit=10):
key = f"rate:{user_id}:{time.time() // 60}" # Per minute
count = redis.incr(key)
redis.expire(key, 60)
return count <= limit
- Simple, but edge case: burst at window boundary
2. Sliding Window (Token Bucket):
local key = KEYS[1]
local limit = tonumber(ARGV[1])
local window = tonumber(ARGV[2])
local now = tonumber(ARGV[3])
redis.call('ZREMRANGEBYSCORE', key, 0, now - window)
local count = redis.call('ZCARD', key)
if count < limit then
redis.call('ZADD', key, now, now)
redis.call('EXPIRE', key, window)
return 1
else
return 0
end
- Accurate, smooth limiting (requests spread evenly)
3. Leaky Bucket:
def is_allowed(user_id, limit=10, window=60):
key = f"rate:{user_id}"
pipe = redis.pipeline()
now = time.time()
# Remove old entries
pipe.zremrangebyscore(key, 0, now - window)
# Count remaining
pipe.zcard(key)
# Add current request
pipe.zadd(key, {now: now})
# Set expiration
pipe.expire(key, window)
results = pipe.execute()
return results[1] < limit
Q: How do you handle Redis failover in production?
A: Multi-layered approach:
1. Redis Sentinel Setup:
# 3+ Sentinel instances for quorum
sentinel monitor mymaster 127.0.0.1 6379 2
sentinel down-after-milliseconds mymaster 5000
sentinel parallel-syncs mymaster 1
sentinel failover-timeout mymaster 10000
2. Client configuration:
from redis.sentinel import Sentinel
sentinel = Sentinel([
('sentinel1', 26379),
('sentinel2', 26379),
('sentinel3', 26379)
], socket_timeout=0.1)
# Always gets current master
master = sentinel.master_for('mymaster', socket_timeout=0.1)
# Read from slaves
slave = sentinel.slave_for('mymaster', socket_timeout=0.1)
3. Application resilience:
- Connection pooling (reuse connections)
- Retry logic with exponential backoff (retry on failures)
- Circuit breaker pattern (stop on repeated failures)
- Graceful degradation (continue without cache if Redis is down)
4. Monitoring:
- Continuous health checks (track uptime)
- Alert on failover events (notifications)
- Track replication lag (slaves catching up)
- Monitor client connections (connection spikes)
Q: What are common Redis anti-patterns?
A:
- Large keys: Storing huge values (>1MB) blocks Redis (split into smaller chunks)
- KEYS command in production: Scans all keys (use SCAN instead - non-blocking)
- Unbounded lists: Lists growing infinitely (use LTRIM to limit)
- Hot keys: Single key accessed by everyone (shard or replicate)
- No expiration: Keys never expire (memory fills up - set TTL)
- Synchronous operations: Blocking commands (use async, pipelining)
- Connection per request: Expensive (use connection pool)
- No monitoring: Can't debug issues (metrics mandatory)
REAL INTERVIEW SCENARIOS
Challenge 1: Implement distributed lock
import redis
import uuid
import time
class RedisLock:
def __init__(self, redis_client, key, timeout=10):
self.redis = redis_client
self.key = f"lock:{key}"
self.timeout = timeout
self.identifier = str(uuid.uuid4())
def acquire(self):
end_time = time.time() + self.timeout
while time.time() < end_time:
if self.redis.set(self.key, self.identifier, nx=True, ex=self.timeout):
return True
time.sleep(0.001)
return False
def release(self):
# Lua script for atomic check-and-delete
lua_script = """
if redis.call("get", KEYS[1]) == ARGV[1] then
return redis.call("del", KEYS[1])
else
return 0
end
"""
self.redis.eval(lua_script, 1, self.key, self.identifier)
# Usage
lock = RedisLock(redis_client, "resource:123")
if lock.acquire():
try:
# Critical section
process_resource()
finally:
lock.release()
Challenge 2: Leaderboard with Redis Sorted Set
class Leaderboard:
def __init__(self, redis_client, name):
self.redis = redis_client
self.key = f"leaderboard:{name}"
def add_score(self, player, score):
self.redis.zadd(self.key, {player: score})
def increment_score(self, player, amount):
self.redis.zincrby(self.key, amount, player)
def get_rank(self, player):
# Rank (0-based, descending)
rank = self.redis.zrevrank(self.key, player)
return rank + 1 if rank is not None else None
def get_top(self, n=10):
return self.redis.zrevrange(self.key, 0, n-1, withscores=True)
def get_around(self, player, radius=2):
rank = self.redis.zrevrank(self.key, player)
if rank is None:
return []
start = max(0, rank - radius)
end = rank + radius
return self.redis.zrevrange(self.key, start, end, withscores=True)
Challenge 3: Session management
import json
from datetime import timedelta
class SessionManager:
def __init__(self, redis_client, timeout=3600):
self.redis = redis_client
self.timeout = timeout
def create_session(self, session_id, user_data):
key = f"session:{session_id}"
self.redis.setex(key, self.timeout, json.dumps(user_data))
def get_session(self, session_id):
key = f"session:{session_id}"
data = self.redis.get(key)
if data:
# Refresh TTL on access
self.redis.expire(key, self.timeout)
return json.loads(data)
return None
def update_session(self, session_id, user_data):
key = f"session:{session_id}"
if self.redis.exists(key):
self.redis.setex(key, self.timeout, json.dumps(user_data))
return True
return False
def delete_session(self, session_id):
key = f"session:{session_id}"
self.redis.delete(key)
QUESTIONS TO ASK THE INTERVIEWER
Technical:
- "What's your Redis deployment setup? Standalone, Sentinel, or Cluster?"
- "How do you handle cache invalidation strategies?"
- "What's your approach to Redis persistence (RDB/AOF)?"
- "How do you monitor Redis performance and health?"
- "What's your Redis version and upgrade strategy?"
Operational:
- "How do you handle Redis failover and disaster recovery?"
- "What's your strategy for memory management and eviction?"
- "How do you handle Redis backups and restore procedures?"
- "What's the typical Redis workload (read/write ratio)?"
- "How do you ensure Redis security (auth, encryption)?"
QUICK PREP CHECKLIST
Day 1-2: Basics
- [ ] Data types and commands
- [ ] Persistence (RDB/AOF)
- [ ] Expiration and eviction
- [ ] Build simple cache
Day 3-4: Intermediate
- [ ] Transactions and pipelining
- [ ] Pub/Sub messaging
- [ ] Lua scripting
- [ ] Implement rate limiter
Day 5-6: Advanced
- [ ] Replication and Sentinel
- [ ] Redis Cluster
- [ ] Performance tuning
- [ ] Build distributed lock
Day 7: Review
- [ ] Review concepts
- [ ] Practice scenarios
- [ ] Prepare questions
Conclusion
Redis is a powerful tool that goes far beyond simple caching. Understanding its data structures, persistence options, and advanced features will help you build scalable systems and ace your interviews.
The key is to practice. Set up a local Redis instance, try the examples in this guide, and build something with it. The more hands-on experience you have, the more confident you'll be in interviews and production environments.
Good luck with your Redis interviews!
Further Reading
- Understanding Connections and Threads in Backend Services - Complete guide on how single-threaded systems handle high concurrency
- 6 Common Redis and Kafka Challenges I Faced and How I Solved Them - Real-world challenges and solutions
- Redis Official Documentation
- Redis University - Free courses
Tags: #redis #interview #backend #database #career #programming
Top comments (0)