DEV Community

Ricky512227
Ricky512227

Posted on

Redis Interview Preparation Guide: From Basics to Hero

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:

  1. String: Simple key-value (basic type - text, numbers, binary)
SET user:1:name "John"
GET user:1:name
INCR view_count
Enter fullscreen mode Exit fullscreen mode
  1. List: Ordered collection (array-like - push/pop from both ends)
LPUSH tasks "task1"
RPUSH tasks "task2"
LPOP tasks
Enter fullscreen mode Exit fullscreen mode
  1. Set: Unique unordered collection (no duplicates - unique values)
SADD tags "python" "redis"
SMEMBERS tags
SISMEMBER tags "python"
Enter fullscreen mode Exit fullscreen mode
  1. 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
Enter fullscreen mode Exit fullscreen mode
  1. Sorted Set: Set with scores (maintains ranking - perfect for leaderboards)
ZADD leaderboard 100 "player1"
ZADD leaderboard 200 "player2"
ZRANGE leaderboard 0 -1 WITHSCORES
Enter fullscreen mode Exit fullscreen mode

Q: How does Redis achieve high performance?

A: Multiple factors contribute:

  1. In-memory storage: No disk I/O for reads (direct from RAM - microseconds)
  2. Single-threaded: No context switching, no locks (simple execution model - automatic thread safety)
  3. Optimized data structures: Efficient implementations (memory-optimized structures)
  4. Event-driven: Non-blocking I/O (async operations - one thread handles many requests)
  5. 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)
  • appendfsync options:
    • 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:

  1. noeviction: Return errors (doesn't delete - returns error)
  2. allkeys-lru: Remove least recently used (delete old data from all keys - most common)
  3. allkeys-lfu: Remove least frequently used (delete less frequently used data)
  4. volatile-lru: LRU among keys with TTL (from keys with expire set)
  5. volatile-lfu: LFU among keys with TTL
  6. volatile-random: Random among keys with TTL
  7. volatile-ttl: Remove keys with shortest TTL (keys expiring soon first)
  8. 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
Enter fullscreen mode Exit fullscreen mode

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"
Enter fullscreen mode Exit fullscreen mode

2. Enable compression:

  • Use ziplist encoding (small lists/hashes stored efficiently - memory compact)

3. Set TTL on everything:

SETEX key 3600 value  # Auto cleanup
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode
  • 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
Enter fullscreen mode Exit fullscreen mode
  • 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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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)
Enter fullscreen mode Exit fullscreen mode

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:

  1. Large keys: Storing huge values (>1MB) blocks Redis (split into smaller chunks)
  2. KEYS command in production: Scans all keys (use SCAN instead - non-blocking)
  3. Unbounded lists: Lists growing infinitely (use LTRIM to limit)
  4. Hot keys: Single key accessed by everyone (shard or replicate)
  5. No expiration: Keys never expire (memory fills up - set TTL)
  6. Synchronous operations: Blocking commands (use async, pipelining)
  7. Connection per request: Expensive (use connection pool)
  8. 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()
Enter fullscreen mode Exit fullscreen mode

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)
Enter fullscreen mode Exit fullscreen mode

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)
Enter fullscreen mode Exit fullscreen mode

QUESTIONS TO ASK THE INTERVIEWER

Technical:

  1. "What's your Redis deployment setup? Standalone, Sentinel, or Cluster?"
  2. "How do you handle cache invalidation strategies?"
  3. "What's your approach to Redis persistence (RDB/AOF)?"
  4. "How do you monitor Redis performance and health?"
  5. "What's your Redis version and upgrade strategy?"

Operational:

  1. "How do you handle Redis failover and disaster recovery?"
  2. "What's your strategy for memory management and eviction?"
  3. "How do you handle Redis backups and restore procedures?"
  4. "What's the typical Redis workload (read/write ratio)?"
  5. "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


Tags: #redis #interview #backend #database #career #programming

Top comments (0)